Marked libisofs SVN copy as outdated (use bzr on lp)

This commit is contained in:
2010-02-08 11:10:47 +00:00
parent 7d413dcb12
commit 517adbd8e5
476 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,2 @@
Mario Danic

View File

@ -0,0 +1,4 @@
Joe Neeman
Philippe Rouquier
Suriyan Laohaprapanon
Vreixo Formoso Lopes

View File

@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,19 @@
Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net>
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
Mario Danic <mario.danic@gmail.com>, Thomas Schmitt <scdbackup@gmx.net>
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

View File

@ -0,0 +1,13 @@
Development
===========
- Support for reading of plain iso images.
- Support for reading RR extensions
Version 0.2.8
=============
TODO

View File

@ -0,0 +1,234 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -0,0 +1,167 @@
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 $</* $(webhost):$(webpath)
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 = \
$(libisofs_libisofs_la_SOURCES) \
$(test_iso_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 = \
libisofs-5.pc
EXTRA_DIST = \
libisofs-5.pc.in \
version.h.in \
doc/comments \
doc/doxygen.conf.in \
README \
AUTHORS \
CONTRIBUTORS \
COPYRIGHT \
COPYING \
NEWS \
INSTALL \
ChangeLog

View File

@ -0,0 +1 @@
nothing here now

View File

@ -0,0 +1,256 @@
------------------------------------------------------------------------------
libburnia-project.org
------------------------------------------------------------------------------
This all is under GPL.
(See GPL reference, our clarification and commitment at the end of this text)
------------------------------------------------------------------------------
libburnia-project.org
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt
Still containing parts of
Libburn. By Derek Foreman <derek@signalmarketing.com> and
Ben Jansens <xor@orodu.net>
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

View File

@ -0,0 +1,64 @@
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

View File

@ -0,0 +1,22 @@
AC_DEFUN([TARGET_SHIZZLE],
[
ARCH=""
AC_MSG_CHECKING([target operating system])
case $target in
*-*-linux*)
ARCH=linux
LIBBURN_ARCH_LIBS=
;;
*-*-freebsd*)
ARCH=freebsd
LIBBURN_ARCH_LIBS=-lcam
;;
*)
AC_ERROR([You are attempting to compile for an unsupported platform])
;;
esac
AC_MSG_RESULT([$ARCH])
])

View File

@ -0,0 +1,10 @@
#!/bin/sh -x
aclocal
libtoolize --copy --force
autoconf
# ts A61101 : libburn is not prepared for config.h
# autoheader
automake --foreign --add-missing --copy --include-deps

View File

@ -0,0 +1,117 @@
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 <time.h>])
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

View File

@ -0,0 +1,4 @@
all clean:
$(MAKE) -C .. -$(MAKEFLAGS) $@
.PHONY: all clean

View File

@ -0,0 +1,123 @@
/**
@author Mario Danic, Thomas Schmitt
@mainpage Libburn Documentation Index
@section intro Introduction
Libburn is an open-source library for reading, mastering and writing
optical discs. For now this means only CD-R and CD-RW.
The project comprises of several more or less interdependent parts which
together strive to be a usable foundation for application development.
These are libraries, language bindings, and middleware binaries which emulate
classical (and valuable) Linux tools.
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
we would need : login on a development machine resp. a live OS on CD or DVD,
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
volunteers for testing of realistic use cases.
We do have a workable code base for burning data CDs, though. The burn API is
quite comprehensively documented and can be used to build a presentable
application.
We do have a functional binary which emulates parts of cdrecord in order to
prove that usability, and in order to allow you to explore libburn's scope
by help of existing cdrecord frontends.
@subsection components The project components (list subject to growth, hopefully):
- libburn is the library by which preformatted data get onto optical media.
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
/dev/hdX (e.g. on kernel 2.6).
libburn is the foundation of our cdrecord emulation.
- libisofs is the library to pack up hard disk files and directories into a
ISO 9660 disk image. This may then be brought to CD via libburn.
libisofs is to be the foundation of our upcoming mkisofs emulation.
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
cdrecord is a powerful GPL'ed burn program included in Joerg
Schilling's cdrtools. cdrskin strives to be a second source for
the services traditionally provided by cdrecord.
cdrskin does not contain any bytes copied from cdrecord's sources.
Many bytes have been copied from the message output of cdrecord
runs, though.
See cdrskin/README for more.
- "test" is a collection of application gestures and examples given by the
authors of the library features. The main API example of libburn
is named test/libburner.c .
Explore these examples if you look for inspiration.
We plan to be a responsive upstream. Bear with us.
@section using Using the libraries
Our build system is based on autotools.
User experience tells us that you will need at least autotools version 1.7.
To build libburn and its subprojects it should be sufficient to go into
its toplevel directory and execute
- ./bootstrap (needed if you downloaded from SVN)
- ./configure
- make
To make the libraries accessible for running resp. developing applications
- make install
Both libraries are written in C language and get built by autotools.
Thus we expect them to be useable by a wide range of Linux-implemented
languages and development tools.
@section libburner Libburner
libburner is a minimal demo application for the library libburn
(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
It can list the available devices, can blank a CD-RW and
can burn to CD-R or CD-RW.
It's main purpose, nevertheless, is to show you how to use libburn and also
to serve the libburn team as reference application. libburner does indeed
define the standard way how above three gestures can be implemented and
stay upward compatible for a good while.
@subsection libburner-help Libburner --help
<pre>
Usage: test/libburner
[--drive <address>|<driveno>|"-"]
[--blank_fast|--blank_full] [--audio]
[--try_to_simulate] [--stdin_size <bytes>]
[<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:
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.
</pre>
@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.
*/

View File

@ -0,0 +1,121 @@
/**
@author Mario Danic, Thomas Schmitt
@mainpage Libburn Documentation Index
@section intro Introduction
Libburn is an open-source library for reading, mastering and writing
optical discs. For now this means only CD-R and CD-RW.
The project comprises of several more or less interdependent parts which
together strive to be a usable foundation for application development.
These are libraries, language bindings, and middleware binaries which emulate
classical (and valuable) Linux tools.
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
this for now, because of our history. The project could need advise from or
membership of skilled kernel people and people who know how to talk CD/DVD
drives into doing things.
We do have a workable code base for burning data CDs, though. The burn API is
quite comprehensively documented and can be used to build a presentable
application.
We do have a functional binary which emulates parts of cdrecord in order to
prove that usability, and in order to allow you to explore libburn's scope
by help of existing cdrecord frontends.
@subsection components The project components (list subject to growth, hopefully):
- libburn is the library by which preformatted data get onto optical media.
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
/dev/hdX (e.g. on kernel 2.6).
libburn is the foundation of our cdrecord emulation.
- libisofs is the library to pack up hard disk files and directories into a
ISO 9660 disk image. This may then be brought to CD via libburn.
libisofs is to be the foundation of our upcoming mkisofs emulation.
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
cdrecord is a powerful GPL'ed burn program included in Joerg
Schilling's cdrtools. cdrskin strives to be a second source for
the services traditionally provided by cdrecord.
cdrskin does not contain any bytes copied from cdrecord's sources.
Many bytes have been copied from the message output of cdrecord
runs, though.
See cdrskin/README for more.
- "test" is a collection of application gestures and examples given by the
authors of the library features. The main API example of libburn
is named test/libburner.c .
Explore these examples if you look for inspiration.
We plan to be a responsive upstream. Bear with us.
@section using Using the libraries
Our build system is based on autotools.
User experience tells us that you will need at least autotools version 1.7.
To build libburn and its subprojects it should be sufficient to go into
its toplevel directory and execute
- ./bootstrap (needed if you downloaded from SVN)
- ./configure
- make
To make the libraries accessible for running resp. developing applications
- make install
Both libraries are written in C language and get built by autotools.
Thus we expect them to be useable by a wide range of Linux-implemented
languages and development tools.
@section libburner Libburner
libburner is a minimal demo application for the library libburn
(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
It can list the available devices, can blank a CD-RW and
can burn to CD-R or CD-RW.
It's main purpose, nevertheless, is to show you how to use libburn and also
to serve the libburn team as reference application. libburner does indeed
define the standard way how above three gestures can be implemented and
stay upward compatible for a good while.
@subsection libburner-help Libburner --help
<pre>
Usage: test/libburner
[--drive <address>|<driveno>|"-"]
[--verbose <level>] [--blank_fast|--blank_full]
[--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]
[<imagefile>|"-"]
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.
</pre>
@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
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libisofs
Description: ISO9660 filesystem creation library
Version: @VERSION@
Requires:
Libs: -L${libdir} -lisofs
Cflags: -I${includedir}/libisofs

View File

@ -0,0 +1,4 @@
all clean:
$(MAKE) -C .. -$(MAKEFLAGS) $@
.PHONY: all clean

View File

@ -0,0 +1,107 @@
/*
* Contains a simple implementation of a data source that reads from a
* given file.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#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);
}
}

View File

@ -0,0 +1,966 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <err.h>
#include <locale.h>
#include <langinfo.h>
#include <limits.h>
#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;
}

View File

@ -0,0 +1,320 @@
/* -*- 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 <sys/time.h>
#include <stdint.h>
#include <stdio.h> /* for FILE */
#include <sys/types.h>
#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 */

View File

@ -0,0 +1,888 @@
/*
* 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 <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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;
}

View File

@ -0,0 +1,76 @@
/*
* ecma119_read.h
*
* Defines structures for reading from a iso image.
*/
#ifndef ECMA119_READ_H_
#define ECMA119_READ_H_
#include <stdint.h>
#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_*/

View File

@ -0,0 +1,316 @@
/*
* This file contains functions related to the reading of SUSP and
* Rock Ridge extensions on an ECMA-119 image.
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}

View File

@ -0,0 +1,144 @@
/*
* 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_*/

View File

@ -0,0 +1,656 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <assert.h>
#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);
}

View File

@ -0,0 +1,103 @@
/* 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 <sys/stat.h>
#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 */

View File

@ -0,0 +1,619 @@
#include "libisofs.h"
#include "eltorito.h"
#include "volume.h"
#include "util.h"
#include "messages.h"
#include <assert.h>
#include <malloc.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
/**
* 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);
}

View File

@ -0,0 +1,83 @@
#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_*/

View File

@ -0,0 +1,29 @@
#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);
}

View File

@ -0,0 +1,30 @@
#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 */

View File

@ -0,0 +1,254 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#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;
}

View File

@ -0,0 +1,74 @@
/* -*- 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 <stdint.h>
#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_*/

View File

@ -0,0 +1,306 @@
/*
* Implementation of iso_file_src for:
*
* a) read from local filesystem files
* b) read from previous session / image files
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#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;
}

View File

@ -0,0 +1,94 @@
/*
* 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 <sys/types.h>
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_*/

View File

@ -0,0 +1,158 @@
#include <stdlib.h>
#include <string.h>
#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);
}
}

View File

@ -0,0 +1,46 @@
#ifndef ISO_HASH_H
#define ISO_HASH_H
struct iso_hash_node {
struct iso_hash_node *next;
char *path;
};
#define HASH_NODES 128
/**
* Searches in the hash table if the path exists.
*
* \param table The hash table.
* \param path The path of the file to look for.
*
* \return 1 if the path exists in the hash table, 0 otherwise.
*/
int iso_hash_lookup(struct iso_hash_node **table, const char *path);
/**
* Insert a new path in the hash table.
*
* \param table The hash table.
* \param path The path of a file to add to the hash table.
*
* \return 1 if the file wasn't already in the hash table, 0 otherwise.
*/
int iso_hash_insert(struct iso_hash_node **table, const char *path);
/**
* Remove a path from the hash table.
*
* \param table The hash table.
* \param path The path of a file to remove from the hash table.
*
* \return 1 if the file was found and removed, 0 otherwise.
*/
int iso_hash_remove(struct iso_hash_node **table, const char *path);
/**
* Empty the hash table.
*/
void iso_hash_empty(struct iso_hash_node **table);
#endif /* ISO_HASH_H */

View File

@ -0,0 +1,489 @@
/* -*- 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 <assert.h>
#include <string.h>
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);
}

View File

@ -0,0 +1,97 @@
/* -*- 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 <stdint.h>
#include <stdlib.h>
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 */

View File

@ -0,0 +1,404 @@
/* libiso_msgs
Message handling facility of libiso.
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
/* 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);
}

View File

@ -0,0 +1,454 @@
/* libiso_msgs
Message handling facility of libisofs.
Copyright (C) 2006-2007 Thomas Schmitt <scdbackup@gmx.net>,
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 <pthread.h>
#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 */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,67 @@
/*
* 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_*/

View File

@ -0,0 +1,313 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "rockridge.h"
#include "util.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include "tree.h"
#include "susp.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
/** 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);
}
}
}

View File

@ -0,0 +1,112 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Functions and structures used for Rock Ridge support.
*
* See IEEE P1282, Rock Ridge Interchange Protocol, Draft Standard version
* 1.12 for further details.
*/
#ifndef ISO_ROCKRIDGE_H
#define ISO_ROCKRIDGE_H
struct ecma119_write_target;
struct ecma119_tree_node;
/**
* Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification.
*
* The "ER" System Use Entry is used to uniquely identify a specification
* compliant with SUSP. This method adds to the given tree node "." entry
* the "ER" corresponding to the RR protocol.
*
* See IEEE P1281, section 5.5 and IEEE P1282, section 4.3 for more details.
*/
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PX System Use Entry to the given tree node and, if that node is
* a directory, to its "." and ".." entries. The PX System Use Entry is
* used to add POSIX file attributes, such as access permissions or user and
* group id, to a ECMA 119 directory record.
*
* See IEEE P1282, section 4.1.1 for more details.
*/
void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PN System Use Entry to the given tree node.
* The PN System Use Entry is used to store the device number, and it's
* mandatory if the tree node corresponds to a character or block device.
*
* See IEEE P1282, section 4.1.2 for more details.
*/
void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a SL System Use Entry to the given tree node. This is used to store
* the content of a symbolic link, and is mandatory if the tree node
* indicates a symbolic link.
*
* See IEEE P1282, section 4.1.3 for more details.
*/
void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a NM System Use Entry to the given tree node. The purpose of this
* System Use Entry is to store the content of an Alternate Name to support
* POSIX-style or other names.
*
* See IEEE P1282, section 4.1.4 for more details.
*/
void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
/*
* The next 3 System Use Entries are used to handle Deep Directory
* Hierarchies, i.e., hierarchies where the number of directory levels
* exceed the eight limit of ECMA-119.
*/
/**
* Add to the given tree node a CL System Use Entry, that is used to record
* the new location of a directory which has been relocated.
*
* See IEEE P1282, section 4.1.5.1 for more details.
*/
void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a PL System Use Entry, used to record the location of the original
* parent directory of a directory which has been relocated.
*
* This is special because it doesn't modify the susp fields of the directory
* that gets passed to it; it modifies the susp fields of the ".." entry in
* that directory.
*
* See IEEE P1282, section 4.1.5.2 for more details.
*/
void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add a RE System Use Entry to the given tree node. The purpose of the
* this System Use Entry is to indicate to an RRIP-compliant receiving
* system that the Directory Record in which an "RE" System Use Entry is
* recorded has been relocated from another position in the original
* Directory Hierarchy.
*
* See IEEE P1282, section 4.1.5.3 for more details.
*/
void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
/**
* Add to the given tree node a TF System Use Entry, used to record some
* time stamps related to the file.
*
* See IEEE P1282, section 4.1.6 for more details.
*/
void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
#endif /* ISO_ROCKRIDGE_H */

View File

@ -0,0 +1,254 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "susp.h"
#include "util.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
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; i<susp->n_susp_fields; i++) {
free(susp->susp_fields[i]);
}
if (susp->susp_fields) {
free(susp->susp_fields);
}
memset(susp, 0, sizeof(struct susp_info));
}

View File

@ -0,0 +1,76 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Functions and structures used for SUSP (IEEE 1281).
*
* Please refer to IEEE P1281 System Use Sharing Protocol, draft standard
* version 1.12 for more details.
*/
#ifndef __ISO_SUSP
#define __ISO_SUSP
#include <stdint.h>
/* 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 */

View File

@ -0,0 +1,662 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file tree.c
*
* Implement filesystem trees.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <stdio.h>
#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);
}
}
}

View File

@ -0,0 +1,170 @@
/* 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 <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#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 */

View File

@ -0,0 +1,906 @@
/* -*- 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 <config.h>
#endif
#include <wchar.h>
#include <iconv.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <limits.h>
#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<bytes; i++) {
ret += ((uint32_t) buf[i]) << (i*8);
}
return ret;
}
uint32_t iso_read_msb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<bytes; i++) {
ret += ((uint32_t) buf[bytes-i-1]) << (i*8);
}
return ret;
}
uint32_t iso_read_bb(const uint8_t *buf, int bytes, int *error)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
if (error) {
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
if (v1 != v2)
*error = 1;
}
return v1;
}
char *strcopy(const char *buf, size_t len)
{
char *str;
str = malloc( (len+1) * sizeof(char) );
strncpy(str, buf, len);
str[len] = '\0';
/* remove trailing spaces */
for (len = len-1; str[len] == ' ' && len > 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;
}

View File

@ -0,0 +1,153 @@
/* -*- 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 <stdint.h>
#include <time.h>
#include <wchar.h>
#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 */

View File

@ -0,0 +1,340 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#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;
//}

View File

@ -0,0 +1,53 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet sts=8 ts=8 sw=8 : */
/**
* Extra declarations for use with the iso_volume structure.
*/
#ifndef LIBISO_VOLUME_H
#define LIBISO_VOLUME_H
#include "libisofs.h"
/**
* Data volume.
*/
struct iso_volume
{
int refcount; /**< Number of used references to this
volume. */
struct iso_tree_node_dir *root; /**< Root of the directory tree for the
volume. */
char *volume_id; /**< Volume identifier. */
char *publisher_id; /**< Volume publisher. */
char *data_preparer_id; /**< Volume data preparer. */
char *system_id; /**< Volume system identifier. */
char *application_id; /**< Volume application id */
char *copyright_file_id;
char *abstract_file_id;
char *biblio_file_id;
struct el_torito_boot_catalog *bootcat; /**< El-Torito boot catalog */
};
/**
* A set of data volumes.
*/
struct iso_volset
{
int refcount;
struct iso_volume **volume; /**< The volumes belonging to this
volume set. */
int volset_size; /**< The number of volumes in this
volume set. */
char *volset_id; /**< The id of this volume set, encoded
in the current locale. */
};
#endif /* __ISO_VOLUME */

View File

@ -0,0 +1,33 @@
#!/bin/bash
TEST_ROOT=/tmp/libisofs_test
rm -rf $TEST_ROOT
#create test folders
mkdir -p $TEST_ROOT
mkdir -p $TEST_ROOT/dir1/dir11
chmod 755 $TEST_ROOT/dir1
chmod 755 $TEST_ROOT/dir1/dir11
touch $TEST_ROOT/dir1/dir11/a
echo "This file is to check correct file permissions. set them to 754" > $TEST_ROOT/dir1/permtest
chmod 754 $TEST_ROOT/dir1/permtest
mkdir -p $TEST_ROOT/dir2
ln -s $TEST_ROOT/dir1 "$TEST_ROOT/link to dir1"
echo "README file" > $TEST_ROOT/README
chmod 555 $TEST_ROOT/README
ln -s $TEST_ROOT/README "$TEST_ROOT/link to readme"
echo "No read file" > $TEST_ROOT/no_read
chmod 000 $TEST_ROOT/no_read
if ! make check
then
exit 1
fi
test/test

View File

@ -0,0 +1,4 @@
all clean:
$(MAKE) -C .. -$(MAKEFLAGS) $@
.PHONY: all clean

View File

@ -0,0 +1,164 @@
/* -*- 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 <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <err.h>
#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 <num> 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;
}

View File

@ -0,0 +1,297 @@
import struct
import tree
import sys
voldesc_fmt = "B" "5s" "B" "2041x"
# all these fields are common between the pri and sec voldescs
privoldesc_fmt = "B" "5s" "B" "x" "32s" "32s" "8x" "8s" "32x" "4s" "4s" "4s" "8s" "4s4s" "4s4s" "34s" "128s" \
"128s" "128s" "128s" "37s" "37s" "37s" "17s" "17s" "17s" "17s" "B" "x" "512s" "653x"
# the fields unique to the sec_vol_desc
secvoldesc_fmt = "x" "5x" "x" "B" "32x" "32x" "8x" "8x" "32s" "4x" "4x" "4x" "8x" "4x4x" "4x4x" "34x" "128x" \
"128x" "128x" "128x" "37x" "37x" "37x" "17x" "17x" "17x" "17x" "x" "x" "512x" "653x"
dirrecord_fmt = "B" "B" "8s" "8s" "7s" "B" "B" "B" "4s" "B" # + file identifier, padding field and SU area
pathrecord_fmt = "B" "B" "4s" "2s" # + directory identifier and padding field
def read_bb(str, le, be):
val1, = struct.unpack(le, str)
val2, = struct.unpack(be, str)
if val1 != val2:
print "val1=%d, val2=%d" % (val1, val2)
raise AssertionError, "values are not equal in dual byte-order field"
return val1
def read_bb4(str):
return read_bb(str, "<I4x", ">4xI")
def read_bb2(str):
return read_bb(str, "<H2x", ">2xH")
def read_lsb4(str):
return struct.unpack("<I", str)[0]
def read_lsb2(str):
return struct.unpack("<H", str)[0]
def read_msb4(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

View File

@ -0,0 +1,157 @@
/* -*- 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 <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <err.h>
#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 <num> 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;
}

View File

@ -0,0 +1,302 @@
/* -*- 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 <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <err.h>
#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 <num> 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;
}

View File

@ -0,0 +1,159 @@
/* -*- 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 <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <err.h>
#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 <num> 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;
}

View File

@ -0,0 +1,163 @@
/*
* Little program that reads an existing ISO image and prints its
* contents to stdout.
*/
#include "libisofs.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static void
print_permissions(mode_t mode)
{
char perm[10];
//TODO suid, sticky...
perm[9] = '\0';
perm[8] = mode & S_IXOTH ? 'x' : '-';
perm[7] = mode & S_IWOTH ? 'w' : '-';
perm[6] = mode & S_IROTH ? 'r' : '-';
perm[5] = mode & S_IXGRP ? 'x' : '-';
perm[4] = mode & S_IWGRP ? 'w' : '-';
perm[3] = mode & S_IRGRP ? 'r' : '-';
perm[2] = mode & S_IXUSR ? 'x' : '-';
perm[1] = mode & S_IWUSR ? 'w' : '-';
perm[0] = mode & S_IRUSR ? 'r' : '-';
printf("[%s]",perm);
}
static void
print_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;
}

View File

@ -0,0 +1,30 @@
#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();
}

View File

@ -0,0 +1,28 @@
#ifndef TEST_H_
#define TEST_H_
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <CUnit/Basic.h>
#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_*/

View File

@ -0,0 +1,75 @@
/*
* Unit test for data_source implementation
*/
#include "test.h"
#include "libisofs.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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);
}

View File

@ -0,0 +1,122 @@
/*
* Unit test for ecma119_tree.h
*/
#include "libisofs.h"
#include "tree.h"
#include "test.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
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);
}

View File

@ -0,0 +1,33 @@
/*
* Unit test for exclude.h
*/
#include "exclude.h"
#include "test.h"
#include <assert.h>
#include <stdio.h>
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);
}

View File

@ -0,0 +1,314 @@
/*
* Unit test for file.h
*/
#include "test.h"
#include "file.h"
#include "tree.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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);
}

View File

@ -0,0 +1,57 @@
/*
* Unit test iso reading functions
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#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);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,387 @@
/*
* Unit test for util.h
*
* This test utiliy functions
*
*/
#include <time.h>
#include <locale.h>
#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);
}

View File

@ -0,0 +1,375 @@
/*
* Unit test for volume.h
*/
#include "libisofs.h"
#include "tree.h"
#include "test.h"
#include "volume.h"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
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);
}

View File

@ -0,0 +1,77 @@
# a module to help with handling of filenames, directory trees, etc.
import os
import os.path
import stat
def pathsubtract(a, b):
index = a.find(b)
if index == -1:
return None
res = a[ (index + len(b)): ]
if res.find("/") == 0:
res = res[1:]
return res
# same as C strcmp()
def strcmp(a, b):
if a < b:
return -1
if a > b:
return 1
return 0
class TreeNode:
# path is the location of the file/directory. It is either a full path or
# a path relative to $PWD
def __init__(self, parent, path=".", root=".", isofile=None):
if isofile:
self.root = os.path.abspath(isofile)
self.path = ""
else:
fullpath = os.path.abspath( path )
fullroot = os.path.abspath( root )
self.root = fullroot
self.path = pathsubtract( fullpath, fullroot )
self.parent = parent
self.children = []
if self.path == None:
raise NameError, "Invalid paths %s and %s" % (fullpath, fullroot)
# if this is a directory, add its children recursively
def addchildren(self):
if not stat.S_ISDIR( os.lstat(self.root + "/" + self.path).st_mode ):
return
children = os.listdir( self.root + "/" + self.path )
for child in children:
if self.path:
child = self.path + "/" + child
self.children.append( TreeNode(self, child, self.root) )
for child in self.children:
child.addchildren()
def printAll(self, spaces=0):
print " "*spaces + self.root + "/" + self.path
for child in self.children:
child.printAll(spaces + 2)
def isValidISO1(self):
pass
class Tree:
def __init__(self, root=None, isofile=None):
if isofile:
self.root = TreeNode(parent=None, isofile=isofile)
else:
self.root = TreeNode(parent=None, path=root, root=root)
self.root.addchildren()
def isValidISO1(self):
return root.isValidISO1();
#t = Tree(root=".")
#t.root.printAll()

View File

@ -0,0 +1,3 @@
#define BURN_MAJOR_VERSION @BURN_MAJOR_VERSION@
#define BURN_MINOR_VERSION @BURN_MINOR_VERSION@
#define BURN_MICRO_VERSION @BURN_MICRO_VERSION@

View File

@ -0,0 +1,3 @@
Vreixo Formoso
Mario Danic
Thomas Schmitt

View File

@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,19 @@
Vreixo Formoso <metalpain2002@yahoo.es>,
Mario Danic <mario.danic@gmail.com>,
Thomas Schmitt <scdbackup@gmx.net>
Copyright (C) 2007-2008 Vreixo Formoso, Mario Danic, Thomas Schmitt
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,234 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

View File

@ -0,0 +1,191 @@
pkgconfigdir=$(libdir)/pkgconfig
libincludedir=$(includedir)/libisofs
lib_LTLIBRARIES = libisofs/libisofs.la
## ========================================================================= ##
# Build libraries
libisofs_libisofs_la_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
libisofs_libisofs_la_SOURCES = \
libisofs/builder.h \
libisofs/builder.c \
libisofs/node.h \
libisofs/node.c \
libisofs/tree.h \
libisofs/tree.c \
libisofs/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/xor_encrypt.c \
libisofs/util.h \
libisofs/util.c \
libisofs/util_rbtree.c \
libisofs/util_htable.c \
libisofs/filesrc.h \
libisofs/filesrc.c \
libisofs/ecma119.h \
libisofs/ecma119.c \
libisofs/ecma119_tree.h \
libisofs/ecma119_tree.c \
libisofs/writer.h \
libisofs/buffer.h \
libisofs/buffer.c \
libisofs/rockridge.h \
libisofs/rockridge.c \
libisofs/rockridge_read.c \
libisofs/joliet.h \
libisofs/joliet.c \
libisofs/eltorito.h \
libisofs/eltorito.c \
libisofs/iso1999.h \
libisofs/iso1999.c \
libisofs/data_source.c
libisofs_libisofs_la_LIBADD= \
$(THREAD_LIBS)
libinclude_HEADERS = \
libisofs/libisofs.h
## ========================================================================= ##
## Build demo applications
noinst_PROGRAMS = \
demo/lsl \
demo/cat \
demo/catbuffer \
demo/tree \
demo/find \
demo/ecma119tree \
demo/iso \
demo/isoread \
demo/isocat \
demo/isomodify \
demo/isoms \
demo/isogrow
demo_lsl_CPPFLAGS = -Ilibisofs
demo_lsl_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_lsl_SOURCES = demo/lsl.c
demo_cat_CPPFLAGS = -Ilibisofs
demo_cat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_cat_SOURCES = demo/cat.c
demo_catbuffer_CPPFLAGS = -Ilibisofs
demo_catbuffer_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_catbuffer_SOURCES = demo/cat_buffer.c
demo_tree_CPPFLAGS = -Ilibisofs
demo_tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_tree_SOURCES = demo/tree.c
demo_find_CPPFLAGS = -Ilibisofs
demo_find_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_find_SOURCES = demo/find.c
demo_ecma119tree_CPPFLAGS = -Ilibisofs
demo_ecma119tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_ecma119tree_SOURCES = demo/ecma119_tree.c
demo_iso_CPPFLAGS = -Ilibisofs
demo_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_iso_SOURCES = demo/iso.c
demo_isoread_CPPFLAGS = -Ilibisofs
demo_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_isoread_SOURCES = demo/iso_read.c
demo_isocat_CPPFLAGS = -Ilibisofs
demo_isocat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_isocat_SOURCES = demo/iso_cat.c
demo_isomodify_CPPFLAGS = -Ilibisofs
demo_isomodify_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_isomodify_SOURCES = demo/iso_modify.c
demo_isoms_CPPFLAGS = -Ilibisofs
demo_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
demo_isoms_SOURCES = demo/iso_ms.c
demo_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn
demo_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lburn
demo_isogrow_SOURCES = demo/iso_grow.c
## Build unit test
check_PROGRAMS = \
test/test
test_test_CPPFLAGS = -Ilibisofs
test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit
test_test_LDFLAGS = -L.. -lm
test_test_SOURCES = \
test/test.h \
test/test.c \
test/test_node.c \
test/test_image.c \
test/test_tree.c \
test/test_util.c \
test/test_rockridge.c \
test/test_stream.c \
test/mocked_fsrc.h \
test/mocked_fsrc.c
## ========================================================================= ##
## Build documentation (You need Doxygen for this to work)
docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION)
doc: doc/html
doc/html: doc/doxygen.conf
$(RM) -r doc/html; \
doxygen doc/doxygen.conf;
install-data-local:
if [ -d doc/html ]; then \
$(mkinstalldirs) $(docdir)/html; \
$(INSTALL_DATA) doc/html/* $(docdir)/html; \
fi
uninstall-local:
rm -rf $(docdir)
## ========================================================================= ##
# Extra things
nodist_pkgconfig_DATA = \
libisofs-1.pc
EXTRA_DIST = \
libisofs-1.pc.in \
version.h.in \
doc/doxygen.conf.in \
doc/Tutorial \
README \
AUTHORS \
COPYRIGHT \
COPYING \
NEWS \
INSTALL \
TODO \
ChangeLog \
Roadmap

View File

@ -0,0 +1,20 @@
== unknown ==
Libisofs v0.6.4
===============
-
== Fri Feb 22 2008 ==
Libisofs v0.6.2.1
=================
- FIX: missing buffer.h in tarball
== Thu Feb 14 2008 ==
Libisofs v0.6.2
================
- Initial release

View File

@ -0,0 +1,341 @@
------------------------------------------------------------------------------
libisofs
------------------------------------------------------------------------------
Released under GPL (see COPYING file for details).
Copyright (C) 2008 Vreixo Formoso, Mario Danic, Thomas Schmitt
libisofs is part of the libburnia project (libburnia-project.org)
------------------------------------------------------------------------------
libisofs is a library to create an ISO-9660 filesystem, and supports extensions
like RockRidge or Joliet. It is also a full featured ISO-9660 editor, allowing
you to modify an ISO image or multisession disc, including file addition and
removal, change of file names and attributes, etc
Features:
---------
- Image creation
- Creates ISO-9660 images from local files.
- Support for RockRidge and Joliet extensions.
- Support for ISO-9660:1999 (version 2)
- Support for El-Torito bootable images.
- Full featured edition of file names and attributes on the image.
- Several options to relax ISO-9660 constraints.
- Special options for images intended for distribution (suitable default
modes for files, hiding of real timestamps...)
- Multisession
- Support for growing an existing image
- Full-featured edition of the image files, including: addition of new
files, removing of existent files, moving files, renaming files,
change file attributes (permissions, timestamps...)
- Support for "emulated multisession" or image growing, suitable for non
multisession media such as DVD+RW
- Image modification
- It can create a completely new image from files on another image.
- Full-featured edition of image contents
- Others
- Handling of different input and output charset
- Good integration with libburn for image burning.
- Reliable, good handling of different kind of errors.
Requirements:
-------------
- libburn 0.4.2 headers must be installed at compile time. It is not required
at runtime.
Know bugs:
----------
Multisession and image growing can lead to undesired results in several cases:
a) Images with unsupported features, such as:
- UDF.
- HSF/HFS+ or other Mac extensions.
- El-Torito with multiple entries.
- ECMA-119 with extended attributes, multiple extends per file.
- Non El-Torito boot info.
- zisofs compressed images.
- ...
In all these cases, the resulting new image (or new session) could lack some
features of the original image.
In some cases libisofs will issue warning messages, or even refuse to grow
or modify the image. Others remain undetected. Images created with libisofs
do not have this problems.
b) Bootable El-Torito images may have several problems, that result in a new
image that is not bootable, or that boots from an outdated session. In many
cases it is recommended to add boot info again in the new session.
- isolinux images won't be bootable after a modify. This is because
isolinux images need to have hardcoded the root dir lba. libisofs cannot
know whether an image is an isolinux image or not, so the user is
responsible to tell libisofs that it must patch the image, with the
el_torito_patch_isolinux_image() function. This problem could also exists
on other boot images.
- Most boot images are highly dependent of the image contents, so if the
user moves or removes some files on image it is possible they won't boot
anymore.
- There is no safer way to modify hidden boot images, as the size of the
boot image can't be figured out.
c) Generated images could have different ECMA-119 low level names, due to
different way to mangle names, to new files added that force old files to
be renamed, to different relaxed contraints... This only affect the
ISO-9660 info, not the RR names, so it shouldn't be a problem in most
cases. If your app. relies on low level ISO-9660 names, you will need to
ensure all node names are valid ISO names (maybe together with some
relaxed contraints), otherwise libisofs might arbitrarily change the names.
------------------------------------------------------------------------------
Download, Build and Installation
libisofs code is mantained in a Bazaar repository at Launchpad
(https://launchpad.net/libisofs/). You can download it with:
$ bzr branch lp:libisofs
Our build system is based on autotools. For preparing the build you will need
autotools of at least version 1.7. If you have download the code from the
repository, first of all you need to execute
./autogen.sh
on toplevel dir to execute autotools.
Alternatively you may unpack a release tarball for which you do not need
autotools installed.
To build libisofs it should be sufficient to go into its toplevel directory
and execute
./configure --prefix=/usr
make
To make the libraries accessible for running resp. developing applications
make install
See INSTALL file for further details.
------------------------------------------------------------------------------
Overview of libburnia-project.org
libburnia-project.org is an open-source software project for reading, mastering
and writing optical discs.
For now this means only CD media and all single layer DVD media except DVD+R.
The project comprises of several more or less interdependent parts which
together strive to be a usable foundation for application development.
These are libraries, language bindings, and middleware binaries which emulate
classical (and valuable) Linux tools.
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
we would need : login on a development machine resp. a live OS on CD or DVD,
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
volunteers for testing of realistic use cases.
We have a workable code base for burning CD and most single layer DVD.
The burn API is quite comprehensively documented and can be used to build a
presentable application.
We have a functional binary which emulates parts of cdrecord in order to
prove that usability, and in order to allow you to explore libburnia's scope
by help of existing cdrecord frontends.
The project components (list subject to growth, hopefully):
- libburn is the library by which preformatted data get onto optical media.
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
/dev/hdX (e.g. on kernel 2.6).
libburn is the foundation of our cdrecord emulation. Its code is
independent of cdrecord. Its DVD capabilities are learned from
studying the code of dvd+rw-tools and MMC-5 specs. No code but only
the pure SCSI knowledge has been taken from dvd+rw-tools, though.
- libisofs is the library to pack up hard disk files and directories into a
ISO 9660 disk image. This may then be brought to media via libburn.
libisofs is to be the foundation of our upcoming mkisofs emulation.
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
Cdrecord is a powerful GPL'ed burn program included in Joerg
Schilling's cdrtools. cdrskin strives to be a second source for
the services traditionally provided by cdrecord. Additionally it
provides libburn's DVD capabilities, where only -sao is compatible
with cdrecord.
cdrskin does not contain any bytes copied from cdrecord's sources.
Many bytes have been copied from the message output of cdrecord
runs, though.
See cdrskin/README and man cdrskin/cdrskin.1 for more.
- test is a collection of application gestures and examples given by the
authors of the library features. The main API example for libburn
is test/libburner.c .
Explore these examples if you look for inspiration.
We plan to be a responsive upstream. Bear with us. We are still practicing.
------------------------------------------------------------------------------
Project history as far as known to me:
- Founded in 2002 as it seems. See mailing list archives
http://lists.freedesktop.org/archives/libburn/
The site of this founder team is reachable and offers download of a
(somewhat outdated) tarball and from CVS :
http://icculus.org/burn/
Copyright holders and most probably founders:
Derek Foreman and Ben Jansens.
- I came to using libburn in 2005. Founded the cdrskin project and submitted
necessary patches which were accepted or implemented better. Except one
remaining patch which prevented cdrskin from using vanilla libburn from CVS.
The cdrskin project site is reachable and offers download of the heavily
patched (elsewise outdated) tarball under the name cdrskin-0.1.2 :
http://scdbackup.sourceforge.net/cdrskin_eng.html
It has meanwhile moved to use vanilla libburn.pykix.org , though.
Version 0.1.4 constitutes the first release of this kind.
- In July 2006 our team mate Mario Danic announced a revival of libburn
which by about nearly everybody else was perceived as unfriendly fork.
Derek Foreman four days later posted a message which expressed his
discontent.
The situation first caused me to publically regret it and then - after i
got the opportunity to move in with cdrskin - gave me true reason to
personally apologize to Derek Foreman, Ben Jansens and the contibutors at
icculus.org/burn. Posted to both projects:
http://lists.freedesktop.org/archives/libburn/2006-August/000446.html
http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html
- Mid August 2006 project cdrskin established a branch office in
libburn.pykix.org so that all maintainers of our tools have one single place
to get the current (at least slightely) usable coordinated versions of
everything.
Project cdrskin will live forth independendly for a while but it is committed
to stay in sync with libburn.pykix.org (or some successor, if ever).
cdrskin is also committed to support icculus.org/burn if the pending fork
is made reality by content changes in that project. It will cease to maintain
a patched version of icculus.org/burn though. Precondition for a new
release of cdrskin on base of icculus.org/burn would be the pending
"whitelist patch" therefore.
I would rather prefer if both projects find consense and merge, or at least
cooperate. I have not given up hope totally, yet.
I, personally, will honor any approach.
- 2nd September 2006 the decision is made to strive for a consolidation of
copyright and a commitment to GPL in a reasonable and open minded way.
This is to avoid long term problems with code of unknown origin and
with finding consense among the not so clearly defined group of copyright
claimers and -holders.
libisofs is already claimed sole copyright Mario Danic.
cdrskin and libburner are already claimed sole copyright Thomas Schmitt.
Rewrites of other components will follow and concluded by claiming full
copyright within the group of libburn.pykix.org-copyright holders.
- 16th September 2006 feature freeze for release of libburn-0.2.2 .
- 20th September 2006 release of libburn-0.2.2 .
- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 .
This version of cdrskin is much more cdrecord compatible in repect
to drive addressing and audio features.
- 30th October 2006 release of cdrskin-0.2.4 .
- 13th November 2006 splitting releases of libburn+cdrskin from libisofs.
- 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has
become suitable for unaware frontends as long as they perform only the core
of cdrecord use cases (including open-ended input streams, audio, and
multi-session).
- 28th November 2006 the umbrella project which encloses both, libisofs and
libburn, is now called libburnia. For the origin of this name, see
http://en.wikipedia.org/wiki/Liburnians .
- 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope
is widened to a first class of DVD media: overwriteable single layer types
DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired
by dvd+rw-tools' "poor man" writing facility for this class of media.
Taking a bow towards Andy Polyakov.
- 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with
multi-session and with DAO.
- 12th March 2007 version 0.3.4 supports DVD+R and thus covers all single layer
DVD media. Code for double layer DVD+/-R is implemented but awaits a tester
yet.
- 23th April 2007 version 0.3.6 follows the unanimous opinion of Linux kernel
people that one should not use /dev/sg on kernel 2.6.
- 31st July 2007 version 0.3.8 marks the first anniversary of libburn revival.
We look back on improved stability, a substantially extended list of media
and write modes, and better protection against typical user mishaps.
- 24th October 2007 version 0.4.0 is the foundation of new library libisoburn
and an upcomming integrated application for manipulating and writing
ISO 9660 + Rock Ridge images. cdrskin-0.4.0 got capabilities like growisofs
by these enhancements: growing of overwriteable media and disk files.
Taking again a bow towards Andy Polyakov.
- 26th Januar 2008 version 0.4.2 rectifies the version numbering so that we
reliably release libburn.so.4 as should have been done since libburn-0.3.2.
cdrskin now is by default linked dynamically and does a runtime check
to ensure not to be started with a libburn which is older than itself.
------------------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation. To be exact: version 2 of that License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
Clarification in my name and in the name of Mario Danic, upcoming copyright
holders on toplevel of libburnia. To be fully in effect after the remaining
other copyrighted code has been replaced by ours and by copyright-free
contributions of our friends:
------------------------------------------------------------------------------
We, the copyright holders, agree on the interpretation that
dynamical linking of our libraries constitutes "use of" and
not "derivation from" our work in the sense of GPL, provided
those libraries are compiled from our unaltered code.
Thus you may link our libraries dynamically with applications
which are not under GPL. You may distribute our libraries and
application tools in binary form, if you fulfill the usual
condition of GPL to offer a copy of the source code -altered
or unaltered- under GPL.
We ask you politely to use our work in open source spirit
and with the due reference to the entire open source community.
If there should really arise the case where above clarification
does not suffice to fulfill a clear and neat request in open source
spirit that would otherwise be declined for mere formal reasons,
only in that case we will duely consider to issue a special license
covering only that special case.
It is the open source idea of responsible freedom which will be
decisive and you will have to prove that you exhausted all own
means to qualify for GPL.
For now we are firmly committed to maintain one single license: GPL.
signed: Mario Danic, Thomas Schmitt

View File

@ -0,0 +1,33 @@
>>>>>>>>>> RELEASE 0.6.1 (development) >>>>>>>>>>>>>>>>>>>>>
- Review error severities
OK - Prepare API for stability and compatibility check
- Documentation
>>>>>>>>>> RELEASE 0.6.2 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>>
- Intensive testing and bug fixing
>>>>>>>>>> RELEASE 0.6.3 (development) >>>>>>>>>>>>>>>>>>>>>
- Improves to public tree
-> Expose node extended info. Always compile it.
(little memory cost)
-> Review builder / tree / node relation
-> Optimize storage of children in node?
-> Inode object?
- Expose Builder and Streams
- Implement filters: compression, encryption...
- Consider some kind of plugin system for Builders, Filesystems and Filters.
- ECMA-119, Joliet, and ISO-9660:1999 writers can share most of the code.
Create a new writer as a generalization of these.
- Update Java bindings
>>>>>>>>>>> ......
>>>>>>>>>>> RELEASE 1.0.0 (stable) >>>>>>>>>>>>>>>>>>>>>>>>>>
- UDF
- HFS

View File

@ -0,0 +1,35 @@
FEATURES
========
TODO
====
#00001 (node.h) -> consider adding new timestamps to IsoTreeNode
#00004 (libisofs.h) -> Add a get_mime_type() function.
#00005 (node.c) -> optimize iso_dir_iter_take.
#00006 (libisofs.h) -> define more replace values when adding a node to a dir
#00007 (libisofs.h) -> expose iso_tree_add_new_file
#00008 (data_dource.c) -> guard against partial reads
#00009 (ecma119_tree.c/h) -> add true support for harlinks and inode numbers
#00010 (buffer.c) -> optimize ring buffer
#00011 (ecma119.c) -> guard against bad path table usage with more than 65535 dirs
#00012 (fs_image.c) -> support follow symlinks on imafe filesystem
#00013 (fs_image.c) -> check for unsupported flags when reading a dir record
#00014 (fs_image.c) -> more sanity checks to ensure dir record info is valid
#00015 (fs_image.c) -> take care of CD-ROM XA discs when reading SP entry
#00016 (fs_image.c) -> handle non RR ER entries
#00017 (fs_image.c) -> take advantage of other atts of PVD
#00018 (fs_image.c) -> check if there are more entries in the boot catalog
#00019 (fs_image.c) -> set IsoImage attribs from Joliet SVD?
#00020 (fs_image.c) -> handle RR info in Joliet tree
#00021 (fs_image.c) -> handle RR info in ISO 9660:1999 tree
#00022 (joliet.c) -> support relaxed constraints in joliet filenames
#00024 (libisofs.h) -> option to convert names to lower case for iso reading
#00025 (libisofs.h) -> support for merging old image files
#00026 (libisofs.h) -> add support for "hidden" bootable images.
#00027 (iso1999.h) -> Follow ISO 9660:1999 specs when sorting files
FIXME
=====

View File

@ -0,0 +1,22 @@
AC_DEFUN([TARGET_SHIZZLE],
[
ARCH=""
AC_MSG_CHECKING([target operating system])
case $target in
*-*-linux*)
ARCH=linux
LIBBURN_ARCH_LIBS=
;;
*-*-freebsd*)
ARCH=freebsd
LIBBURN_ARCH_LIBS=-lcam
;;
*)
AC_ERROR([You are attempting to compile for an unsupported platform])
;;
esac
AC_MSG_RESULT([$ARCH])
])

View File

@ -0,0 +1,10 @@
#!/bin/sh -x
aclocal
libtoolize --copy --force
autoconf
# ts A61101 : libburn is not prepared for config.h
# autoheader
automake --foreign --add-missing --copy --include-deps

View File

@ -0,0 +1,155 @@
AC_INIT([libisofs], [0.6.3], [http://libburnia-project.org])
AC_PREREQ([2.50])
dnl AC_CONFIG_HEADER([config.h])
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([subdir-objects])
dnl A61101 This breaks Linux build (makes 32 bit off_t)
dnl http://sourceware.org/autobook/autobook/autobook_96.html says
dnl one must include some config.h and this was a pitfall.
dnl So why dig the pit at all ?
dnl AM_CONFIG_HEADER(config.h)
dnl
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
dnl
dnl CURRENT and AGE describe the binary compatibility interval of a
dnl dynamic library.
dnl See also http://www.gnu.org/software/libtool/manual.html#Interfaces
dnl
dnl The name of the library will be libisofs.so.$CURRENT-$AGE.$AGE.$REV
dnl In the terminology of this file:
dnl CURRENT = LT_CURRENT
dnl REV = LT_REVISION
dnl AGE = LT_AGE
dnl
dnl LT_CURRENT, LT_REVISION and LT_AGE get set directly now.
dnl
dnl SONAME of the emerging library is LT_CURRENT - LT_AGE.
dnl The linker will do no finer checks. Especially no age range check for
dnl the cdrskin binary. If SONAME matches, then the couple starts.
dnl
dnl Therefore a run time check is provided by libisofs function
dnl iso_lib_version(). It returns the major, minor and micro revision of the
dnl library. This means LIBISOFS_*_VERSION kept its second job which does not
dnl comply to the usual ways of configure.ac . I.e. now *officially* this is
dnl the source code release version as announced to the public. It has no
dnl conection to SONAME or libtool version numbering.
dnl It rather feeds the API function iso_lib_version().
dnl
dnl If LIBISOFS_*_VERSION changes, be sure to change AC_INIT above to match.
dnl
LIBISOFS_MAJOR_VERSION=0
LIBISOFS_MINOR_VERSION=6
LIBISOFS_MICRO_VERSION=3
LIBISOFS_VERSION=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION.$LIBISOFS_MICRO_VERSION
AC_SUBST(LIBISOFS_MAJOR_VERSION)
AC_SUBST(LIBISOFS_MINOR_VERSION)
AC_SUBST(LIBISOFS_MICRO_VERSION)
AC_SUBST(LIBISOFS_VERSION)
dnl Libtool versioning
LT_RELEASE=$LIBISOFS_MAJOR_VERSION.$LIBISOFS_MINOR_VERSION
# SONAME = 6 - 0 = 6 . Library name = libisofs.6.0.0
LT_CURRENT=6
LT_REVISION=0
LT_AGE=0
LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
AC_SUBST(LT_RELEASE)
AC_SUBST(LT_CURRENT)
AC_SUBST(LT_REVISION)
AC_SUBST(LT_AGE)
AC_SUBST(LT_CURRENT_MINUS_AGE)
AC_PREFIX_DEFAULT([/usr/local])
test "$prefix" = "NONE" && prefix=$ac_default_prefix
AM_MAINTAINER_MODE
AM_PROG_CC_C_O
AC_C_CONST
AC_C_INLINE
AC_C_BIGENDIAN
dnl Large file support
AC_SYS_LARGEFILE
AC_FUNC_FSEEKO
AC_CHECK_FUNC([fseeko])
if test ! $ac_cv_func_fseeko; then
AC_MSG_ERROR([Libisofs requires largefile support.])
fi
AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL_DEPS)
LIBTOOL="$LIBTOOL --silent"
AC_PROG_INSTALL
AC_CHECK_HEADERS()
dnl Use GNU extensions if available
AC_DEFINE(_GNU_SOURCE, 1)
dnl Check for tm_gmtoff field in struct tm
AC_CHECK_MEMBER([struct tm.tm_gmtoff],
[AC_DEFINE(HAVE_TM_GMTOFF, 1,
[Define this if tm structure includes a tm_gmtoff entry.])],
,
[#include <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 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(LIBBURN_ARCH_LIBS)
dnl Add compiler-specific flags
dnl See if the user wants aggressive optimizations of the code
AC_ARG_ENABLE(debug,
[ --enable-debug Disable aggressive optimizations [default=yes]],
, enable_debug=yes)
if test x$enable_debug != xyes; then
if test x$GCC = xyes; then
CFLAGS="$CFLAGS -O3"
CFLAGS="$CFLAGS -fexpensive-optimizations"
fi
CFLAGS="$CFLAGS -DNDEBUG"
else
if test x$GCC = xyes; then
CFLAGS="$CFLAGS -g -pedantic -Wall"
fi
CFLAGS="$CFLAGS -DDEBUG"
fi
dnl Verbose debug to make libisofs issue more debug messages
AC_ARG_ENABLE(verbose-debug,
[ --enable-verbose-debug Enable verbose debug messages [default=no]],
AC_DEFINE(LIBISOFS_VERBOSE_DEBUG, 1))
AC_CONFIG_FILES([
Makefile
doc/doxygen.conf
version.h
libisofs-1.pc
])
AC_OUTPUT

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "fsource.h"
#include <stdio.h>
#include <stdlib.h>
/*
* 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;
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "buffer.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/*
* 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;
}

View File

@ -0,0 +1,136 @@
/*
* Little program that imports a directory to iso image, generates the
* ecma119 low level tree and prints it.
* Note that this is not an API example, but a little program for test
* purposes.
*/
#include "libisofs.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include "util.h"
#include "filesrc.h"
#include "node.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static void
print_permissions(mode_t mode)
{
char perm[10];
//TODO suid, sticky...
perm[9] = '\0';
perm[8] = mode & S_IXOTH ? 'x' : '-';
perm[7] = mode & S_IWOTH ? 'w' : '-';
perm[6] = mode & S_IROTH ? 'r' : '-';
perm[5] = mode & S_IXGRP ? 'x' : '-';
perm[4] = mode & S_IWGRP ? 'w' : '-';
perm[3] = mode & S_IRGRP ? 'r' : '-';
perm[2] = mode & S_IXUSR ? 'x' : '-';
perm[1] = mode & S_IWUSR ? 'w' : '-';
perm[0] = mode & S_IRUSR ? 'r' : '-';
printf("[%s]",perm);
}
static void
print_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;
}

View File

@ -0,0 +1,176 @@
/*
* Little program to show how to create an iso image from a local
* directory.
*/
#include "libisofs.h"
#include "libburn/libburn.h"
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
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 <num> 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;
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include <stdio.h>
#include <stdlib.h>
#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;
}

View File

@ -0,0 +1,257 @@
/*
* Very simple program to show how to grow an iso image.
*/
#include "libisofs.h"
#include "libburn/libburn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
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;
}

View File

@ -0,0 +1,109 @@
/*
* Little program to show how to modify an iso image.
*/
#include "libisofs.h"
#include "libburn/libburn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
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;
}

View File

@ -0,0 +1,114 @@
/*
* Little program to show how to create a multisession iso image.
*/
#include "libisofs.h"
#include "libburn/libburn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
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;
}

View File

@ -0,0 +1,167 @@
/*
* Little program to output the contents of an iso image.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "libisofs.h"
static void
print_permissions(mode_t mode)
{
char perm[10];
//TODO suid, sticky...
perm[9] = '\0';
perm[8] = mode & S_IXOTH ? 'x' : '-';
perm[7] = mode & S_IWOTH ? 'w' : '-';
perm[6] = mode & S_IROTH ? 'r' : '-';
perm[5] = mode & S_IXGRP ? 'x' : '-';
perm[4] = mode & S_IWGRP ? 'w' : '-';
perm[3] = mode & S_IRGRP ? 'r' : '-';
perm[2] = mode & S_IXUSR ? 'x' : '-';
perm[1] = mode & S_IWUSR ? 'w' : '-';
perm[0] = mode & S_IRUSR ? 'r' : '-';
printf(" %s ",perm);
}
static void
print_type(mode_t mode)
{
switch(mode & S_IFMT) {
case S_IFSOCK: printf("[S] "); break;
case S_IFLNK: printf("[L] "); break;
case S_IFREG: printf("[R] "); break;
case S_IFBLK: printf("[B] "); break;
case S_IFDIR: printf("[D] "); break;
case S_IFIFO: printf("[F] "); break;
}
}
static void
print_file_src(IsoFileSource *file)
{
struct stat info;
char *name;
iso_file_source_lstat(file, &info);
print_type(info.st_mode);
print_permissions(info.st_mode);
//printf(" {%ld,%ld} ", (long)info.st_dev, (long)info.st_ino);
name = iso_file_source_get_name(file);
printf(" %s", name);
free(name);
if (S_ISLNK(info.st_mode)) {
char buf[PATH_MAX];
iso_file_source_readlink(file, buf, PATH_MAX);
printf(" -> %s\n", buf);
}
printf("\n");
}
static void
print_dir(IsoFileSource *dir, int level)
{
int ret, i;
IsoFileSource *file;
struct stat info;
char *sp = alloca(level * 2 + 1);
for (i = 0; i < level * 2; i += 2) {
sp[i] = '|';
sp[i+1] = ' ';
}
sp[level * 2-1] = '-';
sp[level * 2] = '\0';
ret = iso_file_source_open(dir);
if (ret < 0) {
printf ("Can't open dir %d\n", ret);
}
while ((ret = iso_file_source_readdir(dir, &file)) == 1) {
printf("%s", sp);
print_file_src(file);
ret = iso_file_source_lstat(file, &info);
if (ret < 0) {
break;
}
if (S_ISDIR(info.st_mode)) {
print_dir(file, level + 1);
}
iso_file_source_unref(file);
}
iso_file_source_close(dir);
if (ret < 0) {
printf ("Can't print dir\n");
}
}
int main(int argc, char **argv)
{
int result;
IsoImageFilesystem *fs;
IsoDataSource *src;
IsoFileSource *root;
IsoReadOpts *ropts;
if (argc != 2) {
printf ("You need to specify a valid path\n");
return 1;
}
iso_init();
iso_set_msgs_severities("NEVER", "ALL", "");
result = iso_data_source_new_from_file(argv[1], &src);
if (result < 0) {
printf ("Error creating data source\n");
return 1;
}
result = iso_read_opts_new(&ropts, 0);
if (result < 0) {
fprintf(stderr, "Error creating read options\n");
return 1;
}
result = iso_image_filesystem_new(src, ropts, 1, &fs);
iso_read_opts_free(ropts);
if (result < 0) {
printf ("Error creating filesystem\n");
return 1;
}
printf("\nVOLUME INFORMATION\n");
printf("==================\n\n");
printf("Vol. id: %s\n", iso_image_fs_get_volume_id(fs));
printf("Publisher: %s\n", iso_image_fs_get_publisher_id(fs));
printf("Data preparer: %s\n", iso_image_fs_get_data_preparer_id(fs));
printf("System: %s\n", iso_image_fs_get_system_id(fs));
printf("Application: %s\n", iso_image_fs_get_application_id(fs));
printf("Copyright: %s\n", iso_image_fs_get_copyright_file_id(fs));
printf("Abstract: %s\n", iso_image_fs_get_abstract_file_id(fs));
printf("Biblio: %s\n", iso_image_fs_get_biblio_file_id(fs));
printf("\nDIRECTORY TREE\n");
printf("==============\n");
result = fs->get_root(fs, &root);
if (result < 0) {
printf ("Can't get root %d\n", result);
return 1;
}
//print_file_src(root);
print_dir(root, 0);
iso_file_source_unref(root);
fs->close(fs);
iso_filesystem_unref((IsoFilesystem*)fs);
iso_data_source_unref(src);
iso_finish();
return 0;
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2007 Vreixo Formoso
*
* This file is part of the libisofs project; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "libisofs.h"
#include "fsource.h"
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/*
* 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;
}

View File

@ -0,0 +1,107 @@
/*
* Little program that import a directory and prints the resulting iso tree.
*/
#include "libisofs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static void
print_permissions(mode_t mode)
{
char perm[10];
//TODO suid, sticky...
perm[9] = '\0';
perm[8] = mode & S_IXOTH ? 'x' : '-';
perm[7] = mode & S_IWOTH ? 'w' : '-';
perm[6] = mode & S_IROTH ? 'r' : '-';
perm[5] = mode & S_IXGRP ? 'x' : '-';
perm[4] = mode & S_IWGRP ? 'w' : '-';
perm[3] = mode & S_IRGRP ? 'r' : '-';
perm[2] = mode & S_IXUSR ? 'x' : '-';
perm[1] = mode & S_IWUSR ? 'w' : '-';
perm[0] = mode & S_IRUSR ? 'r' : '-';
printf("[%s]",perm);
}
static void
print_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;
}

View File

@ -0,0 +1,506 @@
===============================================================================
LIBISOFS DEVELOPMENT TUTORIAL
===============================================================================
Creation date: 2008-Jan-27
Author: Vreixo Formoso
_______________________________________________________________________________
This is a little tutorial of how to use libisofs library for application
development.
Contents:
---------
1. Introduction
1.1 Library initialization
1.2 Image context
1.3 Error reporting
2. Creating an image
2.1 Image tree manipulation
2.2 Set the write options
2.3 Obtaining a burn_source
3. Image growing and modification
3.1 Growing vs Modification
3.2 Image import
3.3 Generating a new image
4. Bootable images
5. Advanced features
-------------------------------------------------------------------------------
1. Introduction
-------------------------------------------------------------------------------
[TODO some lines about refcounts]
-------------------------------------------------------------------------------
1.1. Library initialization
Before any usage of the library, you have to call
iso_init()
in the same way, when you have finished using the library, you should call
iso_finish()
to free all resources reserved by the library.
-------------------------------------------------------------------------------
1.2. Image context
Libisofs is image-oriented, the core of libisofs usage is the IsoImage object.
Thus, the first you need to do is to get your own IsoImage object:
IsoImage *my_image;
iso_image_new("NEW DISC", &my_image);
An IsoImage is a context for image creation. It holds the files that will be
added to image, other related information and several options to customize
the behavior of libisofs when working with such Image. i.e., an IsoImage is
a context for libisofs operations. As such, you can work with several image
contexts at a time.
-------------------------------------------------------------------------------
1.3. Error reporting
In libisofs error reporting is done in two ways: with the return value of
the functions and with the message queue.
Error codes are negative numbers, defined in "libisofs.h" header. An
error code is associated with a given severity, either "DEBUG", "UPDATE",
"NOTE", "HINT", "WARNING", "SORRY", "FAILURE" and "FATAL". For the meaning
of each severity take a look at private header "libiso_msgs.h". Errors
reported by function return value are always "FAILURE" or "FATAL". Other kind
of errors are only reported with the message queue. You can get the severity
of any error message with iso_error_get_severity() function.
First of all, most libisofs functions return an integer. If such integer is
a negative number, it means the function has returned an error. The error code
and its severity is encoded in the return value (take a look at error codes in
libisofs.h header).
Additionally, libisofs reports most of its errors in a message queue. Error
messages on that queue can be printed directly to stderr or programmatically
retrieved. First of all, you should set the severity threshold over which an
error is printed or enqueued, with function:
iso_set_msgs_severities()
Errors enqueued can be retrieved with function:
iso_obtain_msgs()
Together with the code error, a text message and its severity, this function
also returns the image id. This is an identifier that uniquely identifies a
given image context. You can get the identifier of each IsoImage with the
iso_image_get_msg_id()
and that way distinguish what image has issued the message.
-------------------------------------------------------------------------------
2. Creating an Image
-------------------------------------------------------------------------------
An image is built from a set of files that you want to store together in an
ISO-9660 volume. We call the "iso tree" to the file hierarchy that will be
written to image. The image context, IsoImage, holds that tree, together with
configuration options and other properties of the image, that provide info
about the volume (such as the identifier, author, etc...).
All configuration options and volume properties are set by its corresponding
setters (iso_image_set_volset_id(), iso_image_set_publisher_id()...)
To create an image, you have to follow the following steps:
* Obtain the image context.
See "1.2 Image context" for details of how to obtain the IsoImage.
* Set the desired properties
* Prepare the iso tree with the files you want to add to image.
See "2.1 Image tree manipulation" for details
* Select the options for image generation.
See "2.2 Set the write options"
* Get the burn_source used to actually write the image.
-------------------------------------------------------------------------------
2.1 Image tree manipulation
libisofs maintains in memory a file tree (usually called the iso tree), that
represents the files and directories that will be written later to image. You
are allowed to make whatever changes you want to that tree, just like you do
to any "real" filesystem, before actually write it to image.
Unlike other ISO-9660 mastering tools, you have full control over the file
hierarchy that will be written to image, via the libisofs API. You can add
new files, create any file in image, change its name, attributes, etc The iso
tree behaves just like any other POSIX filesystem.
The root of the iso tree is created automatically when the IsoImage is
allocated, and you can't replace it. To get a reference to it you can use the
function:
iso_image_get_root()
* Iso tree objects
Each file in the image or iso tree is represented by an IsoNode instance. In
the same way a POSIX filesystem has several file types (regular files,
directories, symlinks...), the IsoNode has several subtypes:
IsoNode
|
---------------------------------
| | | |
IsoDir IsoFile IsoSymlink IsoSpecial
where
- IsoDir represents a directory
- IsoFile represents a regular file
- IsoSymlink represents a symbolic linke
- IsoSpecial represents any other POSIX file, i.e. block and character
devices, FIFOs, sockets.
You can obtain the concrete type of an IsoNode with the iso_node_get_type()
function.
Many libisofs functions take or return an IsoNode. Many others, however,
require an specific type. You can safety cast any subtype to an IsoNode
object. In the same way, after ensuring you are dealing with the correct
subtype, you can downcast a given IsoNode to the specific subtype.
IsoDir *dir;
IsoNode *node;
node = (IsoNode*) dir;
if (iso_node_get_type(node) == LIBISO_DIR) {
dir = (IsoDir*) node;
...
}
or with the provided macros:
IsoDir *dir;
IsoNode *node;
node = ISO_NODE(dir);
if (ISO_NODE_IS_DIR(node)) {
dir = ISO_DIR(node);
...
}
* Adding files to the image
Files can be added to the image or iso tree either as new files or as files
from the filesystem.
In the first case, files are created directly on the image. They do not
correspond to any file in the filesystem. Provided functions are:
- iso_tree_add_new_dir()
- iso_tree_add_new_symlink()
- iso_tree_add_new_special()
On the other side, you can add local files to the image, either with the
iso_tree_add_node()
or with
iso_tree_add_dir_rec().
The first is intended to add a single file, while the last can be used to add,
recursively, a full directory (see below for details).
It is important to note that libisofs doesn't store any kind of link between
the IsoNode and the filesystem file it was created from. The above functions
just initialize a newly created IsoNode with the attributes of a given file in
the filesystem. After that, you can move the original file, change its
attributes or even delete it. The IsoNode in the image tree remains with the
original attributes. One exception to this rule are the contents of a regular
file. Libisofs does not make any copy of those contents until they're actually
written to image. Thus, you shouldn't modify, move or delete regular files
after adding them to the IsoImage.
* Recursive directory addition.
One common use case is to add a local directory to the image. While this can
be done with iso_tree_add_node(), handling the addition of directory children
in the application, libisofs provides a function suitable for this case:
iso_tree_add_dir_rec()
that takes care of adding all files inside a directory, recursing on directory
children. By default, this function adds all children. However, it is usual
that you don't want really this. For example, you may want to exclude some
kind of files (backup files, application sockets,...). Libisofs provides
several functions to customize the behavior of that function:
- iso_tree_set_follow_symlinks()
- iso_tree_set_ignore_hidden()
- iso_tree_set_ignore_special()
- iso_tree_add_exclude()
* Operations on iso tree
[TODO briefly explain how to add node, change attributes, ...]
* Replace mode
[TODO]
-------------------------------------------------------------------------------
2.2 Set the write options
Once you have prepared the iso tree, it is time to select the options for the
image writing.
These options affect the characteristics of the filesystem to create in the
image, but also can control how libisofs generates the image.
First of all you have to get an instance of IsoWriteOpts, with the function
iso_write_opts_new()
The several options available can be classified in:
- Extensions to add to the ISO-9660 image:
iso_write_opts_set_rockridge()
iso_write_opts_set_joliet()
iso_write_opts_set_iso1999()
RockRidge is highly recommended, in fact you should use it in all image. Joliet
is needed if you want to use your images in Windows system. Nowadays,
ISO-9660:1999 is no much useful, so in most cases you don't want such
extension.
- ISO-9660 options:
iso_write_opts_set_iso_level()
iso_write_opts_set_omit_version_numbers()
iso_write_opts_set_allow_deep_paths()
iso_write_opts_set_allow_longer_paths()
iso_write_opts_set_max_37_char_filenames()
iso_write_opts_set_no_force_dots()
iso_write_opts_set_allow_lowercase()
iso_write_opts_set_allow_full_ascii()
These control the options for the ISO-9660 filesystem. In most cases you won't
care about them, as it is the RockRidge or Joliet extensions what determine the
properties of the files once the image is mounted.
- File attributes options
iso_write_opts_set_replace_mode()
iso_write_opts_set_default_dir_mode()
iso_write_opts_set_default_file_mode()
iso_write_opts_set_default_uid()
iso_write_opts_set_default_gid()
iso_write_opts_set_replace_timestamps()
iso_write_opts_set_default_timestamp()
iso_write_opts_set_always_gmt()
They allow to set default attributes for files in image, despite of the real
attributes of the file on the local filesystem.
-------------------------------------------------------------------------------
2.3 Obtaining a burn_source
Finally, you get the burn_source used to write the image with the function:
iso_image_create_burn_source()
The returned burn_source is suitable for using with libburn, to directly burn
the image to a disc. Alternatively, you can use burn_source read() to get
the image contents (for example, to write them to a file, pipe...).
Before creating the burn_source, libisofs computes the size of the image, so
the get_size() function of the burn_source always returns the final image
size. It also starts a writing thread. All the operations needed to generate
the image are done by this thread, including read the original files contents.
The image is writing to a FIFO buffer, from which the burn_source will read.
The size of the buffer can be set in advanced with a property of the
IsoWriteOpts struct:
iso_write_opts_set_fifo_size()
You can get the state of the buffer in any moment, with the function:
iso_ring_buffer_get_status()
You can also cancel the writer thread at any time, with the cancel() function
of the burn_source.
-------------------------------------------------------------------------------
3. Image growing and modification
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
3.1 Growing vs Modification
Libisofs is not restricted only to create new images. It can also be used to
modify existing images. It supports two kind of image modifications, that we
have called image growing and image modification:
Image modification consists in generating a new image, based on the contents
of an existing image. In this mode, libisofs takes an image, the users modifies
its contents (adding new files, removing files, changing their names...), and
finally libisofs generates a completely new image.
On the other side, image growing is similar, with the difference that the new
image is dependent on the other, i.e., it refers to files of the other image.
Thus, it can't be mounted without the old image. The purpose of this kind of
images is to increment or add files to a multisession disc. The new image only
contains the new files. Old files are just references to the old image blocks.
The advantage of the growing approach is that the generated image is smaller,
as only the new files are written. This mode is suitable when you just want to
add some files to a very big image, or when dealing with write-once media, such
as CD-R. Both the time and space needed for the modification is much less than
with normal image modify.
The main problem of growing is that the new image needs to be recorded together
with the old image, in order to be mountable. The total size of the image
(old + new) is bigger (even much bigger) than a completely new image. So, if
you plan to distribute an image on Internet, or burn it to a disc, generate a
completely new image is usually a better alternative.
To be able to mount a grown image, the OS needs to now you have appended new
data to the original image. In multisession media (such as CD-R), the new data
is appended as a new session, so the OS can identify this and mount the image
propertly. However, when dealing with non-multisession media (such as DVD+RW)
or plain .iso files, the new data is just appended at the end of the old image,
and the OS has no way to know that the appended data is in fact a "new
session". The method introduced by Andy Polyakov in growisofs can be used in
those cases. It consists in overwrite the volume descriptors of the old image
with a new ones that refer to the newly appended contents.
-------------------------------------------------------------------------------
3.2 Image import
The first thing you need to do in order to modify or grow an image is to import
it, with the function:
iso_image_import()
It takes several arguments.
First, the image context, an IsoImage previously obtained with iso_image_new().
In most cases you will want to use an empty image. However, if you have already
added files to the image, they will be removed and replaced with the contents
of the image being imported.
The second parameter is an IsoDataSource instance. It abstracts the original
image, and it is used by libisofs to access its contents. You are free to
implement your own data source to access image contents. However, libisofs has
a simple implementation suitable for reading images on the local filesystem,
that can be used for import both .iso files and inserted media, via the block
device and POSIX functions. You can get it with
iso_data_source_new_from_file()
The third parameter of iso_image_import() is a pointer to an IsoReadOpts
struct. It holds the options for image reading. You get it with:
iso_read_opts_new()
and after calling iso_image_import() you should free it with
iso_read_opts_free()
Some options are related to select what extensions to read. Default options
are suitable for most users.
iso_read_opts_set_no_rockridge()
iso_read_opts_set_no_joliet()
iso_read_opts_set_no_iso1999()
iso_read_opts_set_preferjoliet()
If RockRidge extensions are not present, many files attributes can't be
obtained. In those cases libisofs uses default values. You have options to
configure what default values to use.
iso_read_opts_set_default_uid()
iso_read_opts_set_default_gid()
iso_read_opts_set_default_permissions()
If the original image has been created in another system with a different
charset, you may want to use:
iso_read_opts_set_input_charset()
to specify the encoding of the file names on image.
Finally, to import multisession images, you should tell libisofs that it must
read the last session. For that, you must set the block where the last session
starts:
iso_read_opts_set_start_block()
The last parameter for iso_image_import(), optional, is a pointer that will
be filled with a library-allocated IsoReadImageFeatures, that lets you access
some information about the image: size, extensions used,...
[TODO: explain that iso_image_import uses dir rec options]
-------------------------------------------------------------------------------
3.3 Generating a new image
After importing the image, the old image tree gets loaded. You are free to
make any changes to it: add new files, remove files, change names or
attributes... Refer to "2.1 Image tree manipulation" for details.
When it is ready, you can now create the new image. The process is the same as
explained in "2.2 Set the write options" and "2.3 Obtaining a burn_source".
However, there are some write options that should be taken into account.
First of all, you must select whether you want to grow or modify the image
(read "3.1 Growing vs Modification" for details). You must call
iso_write_opts_set_appendable()
An appendable image leads to image growing, and a non-appendable image leads
to a completelly new image (modification). An appendable image will be appended
after the old image (in a new session, for example). Thus, in those cases, the
first block of the image is not 0. You should set the correct lba of the first
block with:
iso_write_opts_set_ms_block()
That is usually the "Next Writable Address" on a multisession media, and a
value slightly greater than the old image size on .iso files or DVD+RW media.
You can obtain the old image size with the iso_read_image_features_get_size()
function.
In this last case (i.e., on a non multisession media), you will need to
overwrite the volume descriptors of the old image with the new ones. To do
this you need:
- Allocate a buffer of at least 64 KiBs.
- Initialize it with the first 64 KiBs of the original image.
- Pass the buffer to libisofs with the iso_write_opts_set_overwrite_buf()
option.
- After appending the new image, you have to overwrite the first 64 KiBs of
the original image with the new content of the buffer.
-------------------------------------------------------------------------------
4. Bootable images
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
5. Advanced features
-------------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More