Improved docs, and created 0.3.8 branch

This commit is contained in:
Mario Danic 2007-07-31 07:32:26 +00:00
parent ddc4ccc691
commit 165f73dd30
68 changed files with 10480 additions and 34 deletions

View File

@ -0,0 +1,4 @@
Developers:
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 @@
nothing here now

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,134 @@
pkgconfigdir=$(libdir)/pkgconfig
libincludedir=$(includedir)/libisofs
lib_LTLIBRARIES = libisofs/libisofs.la
## ========================================================================= ##
# Build libraries
libisofs_libisofs_la_LDFLAGS = \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
libisofs_libisofs_la_SOURCES = \
libisofs/tree.h \
libisofs/tree.c \
libisofs/volume.h \
libisofs/volume.c \
libisofs/util.h \
libisofs/util.c \
libisofs/ecma119.c \
libisofs/ecma119.h \
libisofs/ecma119_tree.c \
libisofs/ecma119_tree.h \
libisofs/susp.h \
libisofs/susp.c \
libisofs/rockridge.h \
libisofs/rockridge.c \
libisofs/joliet.c \
libisofs/joliet.h \
libisofs/exclude.c \
libisofs/exclude.h \
libisofs/hash.h \
libisofs/hash.c \
libisofs/file.h \
libisofs/file.c \
libisofs/eltorito.h \
libisofs/eltorito.c
libinclude_HEADERS = \
libisofs/libisofs.h
## ========================================================================= ##
## Build test applications
noinst_PROGRAMS = \
test/iso
test_iso_CPPFLAGS = -Ilibisofs
test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
test_iso_SOURCES = test/iso.c
## Build unit test
check_PROGRAMS = \
test/test
test_test_CPPFLAGS = -Ilibisofs
test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit
test_test_LDFLAGS = -L.. -lm
test_test_SOURCES = \
test/test_exclude.c \
test/test_tree.c \
test/test_ecma119_tree.c \
test/test_file_hashtable.c \
test/test_util.c \
test/test_volume.c \
test/test.c
## ========================================================================= ##
## Build documentation (You need Doxygen for this to work)
webhost = http://libburn-api.pykix.org
webpath = /
docdir = $(DESTDIR)$(prefix)/share/doc/$(PACKAGE)-$(VERSION)
doc: doc/html
doc/html: doc/doxygen.conf
if [ -f ./doc/doc.lock ]; then \
$(RM) -r doc/html; \
doxygen doc/doxygen.conf; \
fi
doc-upload: doc/html
scp -r $</* $(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-1.pc
EXTRA_DIST = \
libisofs-1.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,231 @@
------------------------------------------------------------------------------
libburnia.pykix.org
------------------------------------------------------------------------------
This all is under GPL.
(See GPL reference, our clarification and commitment at the end of this text)
------------------------------------------------------------------------------
libburnia.pykix.org
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
Copyright (C) 2006 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.pykix.org
copyright holders and then libburnia.pykix.org is to be their sole copyright.
This is done to achieve the right to issue the clarification and the
commitment as written at the end of this text.
The rights and merits of the Libburn-copyright holders Derek Foreman and
Ben Jansens will be duely respected.
This libburnia.pykix.org toplevel README (C) 2006 Thomas Schmitt
------------------------------------------------------------------------------
Build and Installation
Our build system is based on autotools. For preparing the build of a SVN
snapshot you will need autotools of at least version 1.7.
Check out from SVN by
svn co http://libburnia-svn.pykix.org/libburn/trunk libburn_pykix
go into directory libburn_pykix and apply autotools by
./bootstrap
Alternatively you may unpack a release tarball for which you do not need
autotools installed.
To build a libburnia.pykix.org subproject it should be sufficient to go
into its toplevel directory (here: "libburn_pykix") and execute
./configure
make
To make the libraries accessible for running resp. developing applications
make install
The other half of the project, libisofs, is hosted in the libburnia SVN, too:
svn co http://libburnia-svn.pykix.org/libisofs/trunk libisofs_pykix
See README file there.
------------------------------------------------------------------------------
Overview of libburnia.pykix.org
libburnia.pykix.org is an open-source software project for reading, mastering
and writing optical discs. For now this means only CD-R and CD-RW.
The project comprises of several more or less interdependent parts which
together strive to be a usable foundation for application development.
These are libraries, language bindings, and middleware binaries which emulate
classical (and valuable) Linux tools.
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
we would need : login on a development machine resp. a live OS on CD or DVD,
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
volunteers for testing of realistic use cases.
We do have a workable code base for burning data CDs, though. The burn API is
quite comprehensively documented and can be used to build a presentable
application.
We do have a functional binary which emulates parts of cdrecord in order to
prove that usability, and in order to allow you to explore libburnia's scope
by help of existing cdrecord frontends.
The project components (list subject to growth, hopefully):
- libburn is the library by which preformatted data get onto optical media.
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
/dev/hdX (e.g. on kernel 2.6).
libburn is the foundation of our cdrecord emulation.
- libisofs is the library to pack up hard disk files and directories into a
ISO 9660 disk image. This may then be brought to CD via libburn.
libisofs is to be the foundation of our upcoming mkisofs emulation.
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
Cdrecord is a powerful GPL'ed burn program included in Joerg
Schilling's cdrtools. cdrskin strives to be a second source for
the services traditionally provided by cdrecord.
cdrskin does not contain any bytes copied from cdrecord's sources.
Many bytes have been copied from the message output of cdrecord
runs, though.
See cdrskin/README for more.
- test is a collection of application gestures and examples given by the
authors of the library features. The main API example for libburn
is test/libburner.c .
Explore these examples if you look for inspiration.
We plan to be a responsive upstream. Bear with us. We are still practicing.
------------------------------------------------------------------------------
Project history as far as known to me:
- Founded in 2002 as it seems. See mailing list archives
http://lists.freedesktop.org/archives/libburn/
The site of this founder team is reachable and offers download of a
(somewhat outdated) tarball and from CVS :
http://icculus.org/burn/
Copyright holders and most probably founders:
Derek Foreman and Ben Jansens.
- I came to using libburn in 2005. Founded the cdrskin project and submitted
necessary patches which were accepted or implemented better. Except one
remaining patch which prevented cdrskin from using vanilla libburn from CVS.
The cdrskin project site is reachable and offers download of the heavily
patched (elsewise outdated) tarball under the name cdrskin-0.1.2 :
http://scdbackup.sourceforge.net/cdrskin_eng.html
It has meanwhile moved to use vanilla libburn.pykix.org , though.
Version 0.1.4 constitutes the first release of this kind.
- In Juli 2006 our team mate Mario Danic announced a revival of libburn
which by about nearly everybody else was perceived as unfriendly fork.
Derek Foreman four days later posted a message which expressed his
discontent.
The situation first caused me to publically regret it and then - after i
got the opportunity to move in with cdrskin - gave me true reason to
personally apologize to Derek Foreman, Ben Jansens and the contibutors at
icculus.org/burn. Posted to both projects:
http://lists.freedesktop.org/archives/libburn/2006-August/000446.html
http://mailman-mail1.webfaction.com/pipermail/libburn-hackers/2006-August/000024.html
- Mid August 2006 project cdrskin established a branch office in
libburn.pykix.org so that all maintainers of our tools have one single place
to get the current (at least slightely) usable coordinated versions of
everything.
Project cdrskin will live forth independendly for a while but it is committed
to stay in sync with libburn.pykix.org (or some successor, if ever).
cdrskin is also committed to support icculus.org/burn if the pending fork
is made reality by content changes in that project. It will cease to maintain
a patched version of icculus.org/burn though. Precondition for a new
release of cdrskin on base of icculus.org/burn would be the pending
"whitelist patch" therefore.
I would rather prefer if both projects find consense and merge, or at least
cooperate. I have not given up hope totally, yet.
I, personally, will honor any approach.
- 2nd September 2006 the decision is made to strive for a consolidation of
copyright and a commitment to GPL in a reasonable and open minded way.
This is to avoid long term problems with code of unknown origin and
with finding consense among the not so clearly defined group of copyright
claimers and -holders.
libisofs is already claimed sole copyright Mario Danic.
cdrskin and libburner are already claimed sole copyright Thomas Schmitt.
Rewrites of other components will follow and concluded by claiming full
copyright within the group of libburn.pykix.org-copyright holders.
- 16th September 2006 feature freeze for release of libburn-0.2.2 .
- 20th September 2006 release of libburn-0.2.2 .
- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 .
This version of cdrskin is much more cdrecord compatible in repect
to drive addressing and audio features.
- 30th October 2006 release of cdrskin-0.2.4 .
- 13th November 2006 splitting releases of libburn+cdrskin from libisofs.
- 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has
become suitable for unaware frontends as long as they perform only the core
of cdrecord use cases (including open-ended input streams, audio, and
multi-session).
- 28th November 2006 the umbrella project which encloses both, libisofs and
libburn, is now called libburnia. For the origin of this name, see
http://en.wikipedia.org/wiki/Liburnians .
------------------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
Clarification in my name and in the name of Mario Danic, upcoming copyright
holders on toplevel of libburnia. To be fully in effect after the remaining
other copyrighted code has been replaced by ours and by copyright-free
contributions of our friends:
------------------------------------------------------------------------------
We, the copyright holders, agree on the interpretation that
dynamical linking of our libraries constitutes "use of" and
not "derivation from" our work in the sense of GPL, provided
those libraries are compiled from our unaltered code.
Thus you may link our libraries dynamically with applications
which are not under GPL. You may distribute our libraries and
application tools in binary form, if you fulfill the usual
condition of GPL to offer a copy of the source code -altered
or unaltered- under GPL.
We ask you politely to use our work in open source spirit
and with the due reference to the entire open source community.
If there should really arise the case where above clarification
does not suffice to fulfill a clear and neat request in open source
spirit that would otherwise be declined for mere formal reasons,
only in that case we will duely consider to issue a special license
covering only that special case.
It is the open source idea of responsible freedom which will be
decisive and you will have to prove that you exhausted all own
means to qualify for GPL.
For now we are firmly committed to maintain one single license: GPL.
signed: Mario Danic, Thomas Schmitt

View File

@ -0,0 +1,29 @@
FEATURES
========
El-Torito
Support for multiple images
HFS/HFS+
CD reading
Multisession
UDF
ISO relaxed contraints
ISO 9660:1998
Support for special files (only dirs, reg. files and symlinks are supported).
TESTS
=====
For all
IMPLEMENTATION
==============
a way to return NULL sources meaning a failure!!
Error message queue
Public API for all things already implemented
Better charset support
default input charset to locale one, no always UTF-8

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,17 @@
libisofs python bindings
========================
The Python bindings are layered on two levels. The low-level 1:1 mapping of
the C API is done through ctypes. The higher level bindings build a
pythonic API on that mapping.
The low-level API is in isofs.core. That file is autogenerated using
pyglet's ctypes code generator, which can be found at:
http://pyglet.googlecode.com/svn/trunk/tools/wraptypes/
Note that this is not a permanent solution. Right now the generated code
is committed to the repository, to facilitate development. In the longer
term, the wraptypes tool should be added to the libburnia repository and
the core binding generated by the build system.

View File

@ -0,0 +1,2 @@
from defines import *
from isofs import IsoFS

View File

@ -0,0 +1,185 @@
'''Wrapper for isofs
Generated with:
../wraptypes/wrap.py -ocore.py -lisofs ../../../libisofs/libisofs.h
Do not modify this file.
'''
__docformat__ = 'restructuredtext'
__version__ = '$Id: wrap.py 738 2007-03-12 04:53:42Z Alex.Holkner $'
import ctypes
from ctypes import *
from ctypes.util import find_library as _find_library
#_libpath = _find_library('isofs')
#if not _libpath:
# raise ImportError('Could not locate isofs library')
# If you are a developer, comment the above three lines and uncomment
# the following line, to hardcode the library path.
_libpath = 'libisofs.so'
_lib = cdll.LoadLibrary(_libpath)
_int_types = (c_int16, c_int32)
if hasattr(ctypes, 'c_int64'):
# Some builds of ctypes apparently do not have c_int64
# defined; it's a pretty good bet that these builds do not
# have 64-bit pointers.
_int_types += (ctypes.c_int64,)
for t in _int_types:
if sizeof(t) == sizeof(c_size_t):
c_ptrdiff_t = t
class c_void(Structure):
# c_void_p is a buggy return type, converting to int, so
# POINTER(None) == c_void_p is actually written as
# POINTER(c_void), so it can be treated as a real pointer.
_fields_ = [('dummy', c_int)]
class struct_iso_volume(Structure):
__slots__ = [
]
struct_iso_volume._fields_ = [
('_opaque_struct', c_int)
]
class struct_iso_tree_node(Structure):
__slots__ = [
]
struct_iso_tree_node._fields_ = [
('_opaque_struct', c_int)
]
class struct_iso_volset(Structure):
__slots__ = [
]
struct_iso_volset._fields_ = [
('_opaque_struct', c_int)
]
class struct_burn_source(Structure):
__slots__ = [
]
struct_burn_source._fields_ = [
('_opaque_struct', c_int)
]
# ../../../libisofs/libisofs.h:45
iso_volume_new = _lib.iso_volume_new
iso_volume_new.restype = POINTER(struct_iso_volume)
iso_volume_new.argtypes = [c_char_p, c_char_p, c_char_p]
# ../../../libisofs/libisofs.h:49
iso_volume_new_with_root = _lib.iso_volume_new_with_root
iso_volume_new_with_root.restype = POINTER(struct_iso_volume)
iso_volume_new_with_root.argtypes = [c_char_p, c_char_p, c_char_p, POINTER(struct_iso_tree_node)]
# ../../../libisofs/libisofs.h:57
iso_volume_free = _lib.iso_volume_free
iso_volume_free.restype = None
iso_volume_free.argtypes = [POINTER(struct_iso_volume)]
# ../../../libisofs/libisofs.h:62
iso_volset_free = _lib.iso_volset_free
iso_volset_free.restype = None
iso_volset_free.argtypes = [POINTER(struct_iso_volset)]
# ../../../libisofs/libisofs.h:67
iso_volume_get_root = _lib.iso_volume_get_root
iso_volume_get_root.restype = POINTER(struct_iso_tree_node)
iso_volume_get_root.argtypes = [POINTER(struct_iso_volume)]
# ../../../libisofs/libisofs.h:72
iso_volume_set_volume_id = _lib.iso_volume_set_volume_id
iso_volume_set_volume_id.restype = None
iso_volume_set_volume_id.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:78
iso_volume_set_publisher_id = _lib.iso_volume_set_publisher_id
iso_volume_set_publisher_id.restype = None
iso_volume_set_publisher_id.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:84
iso_volume_set_data_preparer_id = _lib.iso_volume_set_data_preparer_id
iso_volume_set_data_preparer_id.restype = None
iso_volume_set_data_preparer_id.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:96
iso_tree_volume_path_to_node = _lib.iso_tree_volume_path_to_node
iso_tree_volume_path_to_node.restype = POINTER(struct_iso_tree_node)
iso_tree_volume_path_to_node.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:107
iso_tree_volume_add_path = _lib.iso_tree_volume_add_path
iso_tree_volume_add_path.restype = POINTER(struct_iso_tree_node)
iso_tree_volume_add_path.argtypes = [POINTER(struct_iso_volume), c_char_p, c_char_p]
# ../../../libisofs/libisofs.h:119
iso_tree_volume_add_new_dir = _lib.iso_tree_volume_add_new_dir
iso_tree_volume_add_new_dir.restype = POINTER(struct_iso_tree_node)
iso_tree_volume_add_new_dir.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:128
iso_volset_new = _lib.iso_volset_new
iso_volset_new.restype = POINTER(struct_iso_volset)
iso_volset_new.argtypes = [POINTER(struct_iso_volume), c_char_p]
# ../../../libisofs/libisofs.h:141
iso_tree_add_node = _lib.iso_tree_add_node
iso_tree_add_node.restype = POINTER(struct_iso_tree_node)
iso_tree_add_node.argtypes = [POINTER(struct_iso_tree_node), c_char_p]
# ../../../libisofs/libisofs.h:157
iso_tree_radd_dir = _lib.iso_tree_radd_dir
iso_tree_radd_dir.restype = POINTER(struct_iso_tree_node)
iso_tree_radd_dir.argtypes = [POINTER(struct_iso_tree_node), c_char_p]
# ../../../libisofs/libisofs.h:166
iso_exclude_add_path = _lib.iso_exclude_add_path
iso_exclude_add_path.restype = None
iso_exclude_add_path.argtypes = [c_char_p]
# ../../../libisofs/libisofs.h:173
iso_exclude_remove_path = _lib.iso_exclude_remove_path
iso_exclude_remove_path.restype = None
iso_exclude_remove_path.argtypes = [c_char_p]
# ../../../libisofs/libisofs.h:178
iso_exclude_empty = _lib.iso_exclude_empty
iso_exclude_empty.restype = None
iso_exclude_empty.argtypes = []
# ../../../libisofs/libisofs.h:191
iso_tree_add_new_dir = _lib.iso_tree_add_new_dir
iso_tree_add_new_dir.restype = POINTER(struct_iso_tree_node)
iso_tree_add_new_dir.argtypes = [POINTER(struct_iso_tree_node), c_char_p]
# ../../../libisofs/libisofs.h:197
iso_tree_node_set_name = _lib.iso_tree_node_set_name
iso_tree_node_set_name.restype = None
iso_tree_node_set_name.argtypes = [POINTER(struct_iso_tree_node), c_char_p]
# ../../../libisofs/libisofs.h:204
iso_tree_print = _lib.iso_tree_print
iso_tree_print.restype = None
iso_tree_print.argtypes = [POINTER(struct_iso_tree_node), c_int]
# ../../../libisofs/libisofs.h:220
iso_source_new_ecma119 = _lib.iso_source_new_ecma119
iso_source_new_ecma119.restype = POINTER(struct_burn_source)
iso_source_new_ecma119.argtypes = [POINTER(struct_iso_volset), c_int, c_int, c_int]
__all__ = ['iso_volume_new', 'iso_volume_new_with_root', 'iso_volume_free',
'iso_volset_free', 'iso_volume_get_root', 'iso_volume_set_volume_id',
'iso_volume_set_publisher_id', 'iso_volume_set_data_preparer_id',
'iso_tree_volume_path_to_node', 'iso_tree_volume_add_path',
'iso_tree_volume_add_new_dir', 'iso_volset_new', 'iso_tree_add_node',
'iso_tree_radd_dir', 'iso_exclude_add_path', 'iso_exclude_remove_path',
'iso_exclude_empty', 'iso_tree_add_new_dir', 'iso_tree_node_set_name',
'iso_tree_print', 'iso_source_new_ecma119']

View File

@ -0,0 +1,9 @@
# The automatic code generator does not wrap enums into python. As
# isofs has only two enum values publicly defined, they are hardcoded
# here.
# from enum ecma119_extension_flag
ECMA119_ROCKRIDGE = 1
ECMA119_JOLIET = 2
__all__ = ['ECMA119_ROCKRIDGE', 'ECMA119_JOLIET']

View File

@ -0,0 +1,58 @@
# High level interface to the isofs library.
import core
import defines
import os.path
def _wrap_volume_property(var_name, core_setter):
def get(self):
return getattr(self, var_name)
def set(self, value):
setattr(self, var_name, value)
core_setter(self._volume, value)
return property(get, set)
class IsoFS(object):
def __init__(self, volume_id='', publisher_id='',
data_preparer_id=''):
self._volume_id = volume_id
self._publisher_id = publisher_id
self._data_preparer_id = data_preparer_id
self._volume = core.iso_volume_new(
volume_id, publisher_id, data_preparer_id)
self._volset = core.iso_volset_new(self._volume, volume_id)
def __del__(self):
core.iso_volume_free(self._volume)
core.iso_volset_free(self._volset)
volume_id = _wrap_volume_property(
'_volume_id', core.iso_volume_set_volume_id)
publisher_id = _wrap_volume_property(
'_publisher_id', core.iso_volume_set_publisher_id)
data_preparer_id = _wrap_volume_property(
'_data_preparer_id', core.iso_volume_set_data_preparer_id)
def add(self, disc_path, path, exclude=None): #, make_parents=False):
disc_path = os.path.normpath(disc_path)
path = os.path.abspath(os.path.normpath(path))
exclude = exclude or []
# Does the disc parent exist?
disc_parent, _ = os.path.split(disc_path)
if not core.iso_tree_volume_path_to_node(self._volume, disc_parent):
print "No such parent"
return # TODO: Raise exception and/or create all missing
# parents.
# Flush all ignores that may have stayed over.
core.iso_exclude_empty()
for exclude_path in exclude:
core.iso_exclude_add_path(exclude_path)
core.iso_tree_volume_add_path(self._volume, disc_path, path)
def display(self):
root = core.iso_volume_get_root(self._volume)
core.iso_tree_print(root, 0)

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.4], [http://libburnia.pykix.org])
AC_PREREQ([2.50])
dnl AC_CONFIG_HEADER([config.h])
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([subdir-objects])
dnl A61101 This breaks Linux build (makes 32 bit off_t)
dnl http://sourceware.org/autobook/autobook/autobook_96.html says
dnl one must include some config.h and this was a pitfall.
dnl So why dig the pit at all ?
dnl AM_CONFIG_HEADER(config.h)
dnl Making releases:
dnl BURN_MICRO_VERSION += 1;
dnl BURN_INTERFACE_AGE += 1;
dnl BURN_BINARY_AGE += 1;
dnl if any functions have been added, set BURN_INTERFACE_AGE to 0.
dnl if backwards compatibility has been broken,
dnl set BURN_BINARY_AGE and BURN_INTERFACE_AGE to 0.
dnl
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
dnl
BURN_MAJOR_VERSION=0
BURN_MINOR_VERSION=2
BURN_MICRO_VERSION=4
BURN_INTERFACE_AGE=0
BURN_BINARY_AGE=0
BURN_VERSION=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION.$BURN_MICRO_VERSION
AC_SUBST(BURN_MAJOR_VERSION)
AC_SUBST(BURN_MINOR_VERSION)
AC_SUBST(BURN_MICRO_VERSION)
AC_SUBST(BURN_INTERFACE_AGE)
AC_SUBST(BURN_BINARY_AGE)
AC_SUBST(BURN_VERSION)
dnl Libtool versioning
LT_RELEASE=$BURN_MAJOR_VERSION.$BURN_MINOR_VERSION
LT_CURRENT=`expr $BURN_MICRO_VERSION - $BURN_INTERFACE_AGE`
LT_REVISION=$BURN_INTERFACE_AGE
LT_AGE=`expr $BURN_BINARY_AGE - $BURN_INTERFACE_AGE`
LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
AC_SUBST(LT_RELEASE)
AC_SUBST(LT_CURRENT)
AC_SUBST(LT_REVISION)
AC_SUBST(LT_AGE)
AC_SUBST(LT_CURRENT_MINUS_AGE)
AC_PREFIX_DEFAULT([/usr/local])
test "$prefix" = "NONE" && prefix=$ac_default_prefix
AM_MAINTAINER_MODE
AM_PROG_CC_C_O
AC_C_CONST
AC_C_INLINE
AC_C_BIGENDIAN
dnl Large file support
AC_SYS_LARGEFILE
AC_FUNC_FSEEKO
AC_CHECK_FUNC([fseeko])
if test ! $ac_cv_func_fseeko; then
AC_ERROR([Libisofs requires largefile support.])
fi
AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL_DEPS)
LIBTOOL="$LIBTOOL --silent"
AC_PROG_INSTALL
AC_CHECK_HEADERS()
AC_CHECK_MEMBER([struct tm.tm_gmtoff],
[AC_DEFINE(HAVE_TM_GMTOFF, 1,
[Define this if tm structure includes a tm_gmtoff entry.])],
,
[#include <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-1.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
*/

View File

@ -0,0 +1,186 @@
# Doxyfile 1.2.18
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = @PACKAGE_NAME@
PROJECT_NUMBER = @PACKAGE_VERSION@
OUTPUT_DIRECTORY =
OUTPUT_LANGUAGE = English
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = @top_srcdir@
INTERNAL_DOCS = NO
STRIP_CODE_COMMENTS = NO
CASE_SENSE_NAMES = NO
SHORT_NAMES = NO
HIDE_SCOPE_NAMES = NO
VERBATIM_HEADERS = YES
SHOW_INCLUDE_FILES = YES
JAVADOC_AUTOBRIEF = YES
MULTILINE_CPP_IS_BRIEF = YES
DETAILS_AT_TOP = YES
INHERIT_DOCS = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
DISTRIBUTE_GROUP_DOC = NO
TAB_SIZE = 4
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ALIASES =
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = libisofs doc
FILE_PATTERNS = libisofs.h comments
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH = test
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX = OB OTK _
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = doc/html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 200
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = letter
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_SCHEMA =
XML_DTD =
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DOXYGEN
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
TEMPLATE_RELATIONS = YES
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
GRAPHICAL_HIERARCHY = NO
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024
MAX_DOT_GRAPH_HEIGHT = 1024
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

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,840 @@
/* -*- 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 "ecma119.h"
#include "ecma119_tree.h"
#include "susp.h"
#include "rockridge.h"
#include "joliet.h"
#include "volume.h"
#include "tree.h"
#include "util.h"
#include "file.h"
#include "libisofs.h"
#include "libburn/libburn.h"
#include "eltorito.h"
/* burn-source compatible stuff */
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size);
static off_t
bs_get_size(struct burn_source *bs);
static void
bs_free_data(struct burn_source *bs);
typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
/* return true if the given state is only required for Joliet volumes */
static int
is_joliet_state(enum ecma119_write_state);
static void
next_state(struct ecma119_write_target *t);
/* write t->state_data to the buf, one block at a time */
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
/* writing functions. All these functions assume the buf is large enough */
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
static void
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
static void
write_one_dir_record(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
int file_id,
uint8_t *buf);
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf);
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf);
/* wrapper functions for writing */
static void wr_system_area(struct ecma119_write_target*, uint8_t*);
static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
static void wr_files(struct ecma119_write_target*, uint8_t*);
static const write_fn writers[] =
{
NULL,
wr_system_area,
wr_pri_vol_desc,
el_torito_wr_boot_vol_desc,
joliet_wr_sup_vol_desc,
wr_vol_desc_term,
wr_l_path_table,
wr_m_path_table,
joliet_wr_l_path_table,
joliet_wr_m_path_table,
wr_dir_records,
joliet_wr_dir_records,
el_torito_wr_catalog,
wr_files
};
/* When a writer is created, we
* 1) create an ecma119 tree
* 2) add SUSP fields (if necessary)
* 3) calculate the size and position of all nodes in the tree
* 4) finalize SUSP fields (if necessary)
*/
static void
add_susp_fields_rec(struct ecma119_write_target *t,
struct ecma119_tree_node *node)
{
size_t i;
rrip_add_PX(t, node);
rrip_add_NM(t, node);
rrip_add_TF(t, node);
switch (node->type) {
case ECMA119_FILE:
break;
case ECMA119_SYMLINK:
rrip_add_SL(t, node);
break;
case ECMA119_DIR:
if (node->info.dir.real_parent != node->parent) {
rrip_add_RE(t, node);
rrip_add_PL(t, node);
}
for (i = 0; i < node->info.dir.nchildren; i++) {
add_susp_fields_rec(t, node->info.dir.children[i]);
}
break;
case ECMA119_PLACEHOLDER:
rrip_add_CL(t, node);
break;
default:
// FIXME support for device blocks by uncommenting this
//if (node->iso_self->attrib.st_rdev)
// rrip_add_PN(t, node);
break;
}
susp_add_CE(t, node);
}
static void
add_susp_fields(struct ecma119_write_target *t)
{
susp_add_SP(t, t->root);
rrip_add_ER(t, t->root);
add_susp_fields_rec(t, t->root);
}
/**
* Fill out the dir.len and dir.CE_len fields for each
* ecma119_tree_node that is a directory. Also calculate the total number of
* directories and the number of files for which we need to write out data.
* (dirlist_len and filelist_len)
*/
static void
calc_dir_size(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
size_t newlen;
assert(dir->type == ECMA119_DIR);
t->dirlist_len++;
dir->info.dir.len = 34 + dir->info.dir.self_susp.non_CE_len
+ 34 + dir->info.dir.parent_susp.non_CE_len;
dir->info.dir.CE_len = dir->info.dir.self_susp.CE_len
+ dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; ++i) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
newlen = dir->info.dir.len + ch->dirent_len + ch->susp.non_CE_len;
if ((newlen % 2048) < (dir->info.dir.len % 2048)) {
dir->info.dir.len = newlen + (2048 - (dir->info.dir.len % 2048));
} else {
dir->info.dir.len += ch->dirent_len + ch->susp.non_CE_len;
}
dir->info.dir.CE_len += ch->susp.CE_len;
}
t->total_dir_size += round_up(dir->info.dir.len + dir->info.dir.CE_len,
t->block_size);
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
//struct iso_tree_node *iso = ch->iso_self;
if (ch->type == ECMA119_DIR) {
calc_dir_size(t, ch);
}
// else if (iso && iso->attrib.st_size
// && iso->loc.type == LIBISO_FILESYS
// && iso->loc.path) {
// t->filelist_len++;
// }
}
}
/**
* Fill out the block field in each ecma119_tree_node that is a directory and
* fill out t->dirlist.
*/
static void
calc_dir_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
assert(dir->type == ECMA119_DIR);
dir->info.dir.block = t->curblock;
t->curblock += div_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size);
t->dirlist[t->curfile++] = dir;
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
if (ch->type == ECMA119_DIR)
calc_dir_pos(t, ch);
}
}
static int
cmp_file(const void *f1, const void *f2)
{
struct iso_file *f = *((struct iso_file**)f1);
struct iso_file *g = *((struct iso_file**)f2);
/* higher weighted first */
return g->sort_weight - f->sort_weight;
}
/**
* Fill out the block field for each file and fill out t->filelist.
*/
static void
calc_file_pos(struct ecma119_write_target *t,
struct ecma119_tree_node *dir)
{
size_t i;
assert(dir->type == ECMA119_DIR);
t->filelist = calloc(1, sizeof(struct iso_file *) * t->file_table->count);
for (i = 0; i < FILE_HASH_NODES; ++i) {
struct iso_file_hash_node *node;
node = t->file_table->table[i];
if (!node)
continue;
do {
struct iso_file *file = node->file;
if (file->size)
t->filelist[t->curfile++] = file;
node = node->next;
} while (node);
}
t->filelist_len = t->curfile;
/* sort */
if ( t->sort_files )
qsort(t->filelist, t->filelist_len, sizeof(void*), cmp_file);
/* fill block value */
for ( i = 0; i < t->filelist_len; ++i) {
struct iso_file *file = t->filelist[i];
file->block = t->curblock;
t->curblock += div_up(file->size, t->block_size);
}
/* reset curfile when we're finished */
t->curfile = 0;
}
/**
* Create a new ecma119_write_target from the given volume number of the
* given volume set.
*
* \pre \p volnum is less than \p volset-\>volset_size.
* \post For each node in the tree, writer_data has been allocated.
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
*/
static struct ecma119_write_target*
ecma119_target_new(struct iso_volset *volset,
const struct ecma119_source_opts *opts)
{
struct ecma119_write_target *t =
calloc(1, sizeof(struct ecma119_write_target));
size_t i, j, cur;
struct iso_tree_node *iso_root =
(struct iso_tree_node*) volset->volume[opts->volnum]->root;
t->cache_inodes = opts->no_cache_inodes ? 0 : 1;
t->replace_mode = opts->default_mode ? 0 : 1;
if ( opts->replace_dir_mode )
t->replace_mode |= 0x02;
if ( opts->replace_file_mode )
t->replace_mode |= 0x04;
if ( opts->replace_gid )
t->replace_mode |= 0x08;
if ( opts->replace_uid )
t->replace_mode |= 0x10;
t->dir_mode = opts->dir_mode;
t->file_mode = opts->file_mode;
t->gid = opts->gid;
t->uid = opts->uid;
//TODO get defailt values for current locale, no UTF-8
t->input_charset = opts->input_charset ? opts->input_charset : "UTF-8";
t->ouput_charset = opts->ouput_charset ? opts->ouput_charset : "UTF-8";
t->sort_files = opts->sort_files;
t->file_table = iso_file_table_new(t->cache_inodes);
volset->refcount++;
t->iso_level = opts->level;
t->block_size = 2048;
t->relaxed_constraints = opts->relaxed_constraints;
t->rockridge = (opts->flags & ECMA119_ROCKRIDGE) ? 1 : 0;
t->joliet = (opts->flags & ECMA119_JOLIET) ? 1 : 0;
t->catalog = volset->volume[opts->volnum]->bootcat;
t->eltorito = t->catalog ? 1 : 0;
t->root = ecma119_tree_create(t, iso_root);
if (t->joliet)
t->joliet_root = joliet_tree_create(t, iso_root);
t->volset = volset;
t->volnum = opts->volnum;
t->now = time(NULL);
if (t->rockridge)
add_susp_fields(t);
calc_dir_size(t, t->root);
if (t->joliet) {
joliet_calc_dir_size(t, t->joliet_root);
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len_joliet);
}
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
/* fill out the pathlist */
t->pathlist[0] = t->root;
t->path_table_size = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len; i++) {
struct ecma119_tree_node *dir = t->pathlist[i];
for (j = 0; j < dir->info.dir.nchildren; j++) {
struct ecma119_tree_node *ch = dir->info.dir.children[j];
if (ch->type == ECMA119_DIR) {
size_t len = 8 + strlen(ch->iso_name);
t->pathlist[cur++] = ch;
t->path_table_size += len + len % 2;
}
}
}
t->curblock = 16 /* system area */
+ 1 /* volume desc */
+ 1; /* volume desc terminator */
if (t->eltorito)
t->curblock += 1; /* boot record volume descriptor */
if (t->joliet) /* supplementary vol desc */
t->curblock += div_up (2048, t->block_size);
t->l_path_table_pos = t->curblock;
t->curblock += div_up(t->path_table_size, t->block_size);
t->m_path_table_pos = t->curblock;
t->curblock += div_up(t->path_table_size, t->block_size);
if (t->joliet) {
joliet_prepare_path_tables(t);
t->l_path_table_pos_joliet = t->curblock;
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
t->m_path_table_pos_joliet = t->curblock;
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
}
calc_dir_pos(t, t->root);
/* reset curfile when we're finished */
t->curfile = 0;
if (t->joliet) {
joliet_calc_dir_pos(t, t->joliet_root);
/* reset curfile when we're finished */
t->curfile = 0;
}
/* el-torito? */
if (t->eltorito) {
/* add catalog block */
t->catalog->file->block = t->curblock;
t->curblock += div_up(2048, t->block_size);
el_torito_get_image_files(t);
}
calc_file_pos(t, t->root);
if (t->eltorito)
el_torito_patch_image_files(t);
if (t->rockridge) {
susp_finalize(t, t->root);
rrip_finalize(t, t->root);
}
t->total_size = t->curblock * t->block_size;
t->vol_space_size = t->curblock;
/* prepare for writing */
t->curblock = 0;
t->state = ECMA119_WRITE_SYSTEM_AREA;
return t;
}
static int
is_joliet_state(enum ecma119_write_state state)
{
return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
|| state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
}
static int
is_eltorito_state(enum ecma119_write_state state)
{
return state == ECMA119_WRITE_ELTORITO_BOOT_VOL_DESC
|| state == ECMA119_WRITE_ELTORITO_CATALOG;
}
static void
next_state(struct ecma119_write_target *t)
{
t->state++;
while ( (!t->joliet && is_joliet_state(t->state))
||(!t->eltorito && is_eltorito_state(t->state)) )
t->state++;
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
}
static void
wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
{
memset(buf, 0, t->block_size);
if (t->curblock == 15) {
next_state(t);
}
}
static void
wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
}
static void
wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
}
static void
wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
}
static void
wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
}
static void
wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
{
ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
}
static void
wr_files(struct ecma119_write_target *t, uint8_t *buf)
{
struct state_files *f_st = &t->state_files;
size_t nread;
struct iso_file *f = t->filelist[f_st->file];
const char *path = f->path;
if (!f_st->fd) {
printf("Writing file %s\n", path);
f_st->data_len = f->size;
f_st->fd = fopen(path, "r");
if (!f_st->fd)
err(1, "couldn't open %s for reading", path);
assert(t->curblock == f->block);
}
nread = fread(buf, 1, t->block_size, f_st->fd);
f_st->pos += t->block_size;
if (nread < 0)
warn("problem reading from %s", path);
else if (nread != t->block_size && f_st->pos < f_st->data_len)
warnx("incomplete read from %s", path);
if (f_st->pos >= f_st->data_len) {
fclose(f_st->fd);
f_st->fd = 0;
f_st->pos = 0;
f_st->file++;
if (f_st->file >= t->filelist_len)
next_state(t);
}
}
static void
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
struct iso_volume *volume = t->volset->volume[t->volnum];
char *vol_id = str2d_char(volume->volume_id, t->input_charset);
char *pub_id = str2a_char(volume->publisher_id, t->input_charset);
char *data_id = str2a_char(volume->data_preparer_id, t->input_charset);
char *volset_id = str2d_char(t->volset->volset_id, t->input_charset);
char *system_id = str2a_char(volume->system_id, t->input_charset);
char *application_id = str2a_char(volume->application_id, t->input_charset);
char *copyright_file_id = str2d_char(volume->copyright_file_id, t->input_charset);
char *abstract_file_id = str2d_char(volume->abstract_file_id, t->input_charset);
char *biblio_file_id = str2d_char(volume->biblio_file_id, t->input_charset);
vol->vol_desc_type[0] = 1;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
if (system_id)
strncpy((char*)vol->system_id, system_id, 32);
else
/* put linux by default? */
memcpy(vol->system_id, "LINUX", 5);
if (vol_id)
strncpy((char*)vol->volume_id, vol_id, 32);
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
iso_bb(vol->block_size, t->block_size, 2);
iso_bb(vol->path_table_size, t->path_table_size, 4);
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4);
iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4);
write_one_dir_record(t, t->root, 3, vol->root_dir_record);
/* mmm, why not check for null? */
strncpy((char*)vol->vol_set_id, volset_id, 128);
strncpy((char*)vol->publisher_id, pub_id, 128);
strncpy((char*)vol->data_prep_id, data_id, 128);
if (application_id)
strncpy((char*)vol->application_id, application_id, 128);
if (copyright_file_id)
strncpy((char*)vol->copyright_file_id, copyright_file_id, 37);
if (abstract_file_id)
strncpy((char*)vol->abstract_file_id, abstract_file_id, 37);
if (biblio_file_id)
strncpy((char*)vol->bibliographic_file_id, biblio_file_id, 37);
iso_datetime_17(vol->vol_creation_time, t->now);
iso_datetime_17(vol->vol_modification_time, t->now);
iso_datetime_17(vol->vol_effective_time, t->now);
vol->file_structure_version[0] = 1;
free(vol_id);
free(volset_id);
free(pub_id);
free(data_id);
free(system_id);
free(application_id);
free(copyright_file_id);
free(abstract_file_id);
free(biblio_file_id);
}
static void
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_vol_desc_terminator *vol =
(struct ecma119_vol_desc_terminator*) buf;
vol->vol_desc_type[0] = 255;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
}
static void
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
{
void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb
: iso_msb;
size_t i;
struct ecma119_path_table_record *rec;
struct ecma119_tree_node *dir;
int parent = 0;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist[i];
assert(dir->type == ECMA119_DIR);
while ((i) && t->pathlist[parent] != dir->parent)
parent++;
assert(parent < i || i == 0);
rec = (struct ecma119_path_table_record*) buf;
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->iso_name) : 1;
rec->len_xa[0] = 0;
write_int(rec->block, dir->info.dir.block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->iso_name, rec->len_di[0]);
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table(t, 1, buf);
}
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table(t, 0, buf);
}
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
* file_id == 3 means that we are writing the root directory record (in order
* to distinguish it from the "." entry in the root directory) */
static void
write_one_dir_record(struct ecma119_write_target *t,
struct ecma119_tree_node *node,
int file_id,
uint8_t *buf)
{
uint32_t len;
uint32_t block;
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->iso_name);
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->iso_name;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
if (node->type == ECMA119_DIR) {
len = node->info.dir.len;
block = node->info.dir.block;
} else if (node->type == ECMA119_FILE) {
len = node->info.file->size;
block = node->info.file->block;
} else {
/* for nodes other than files and dirs, we set both len and block to 0 */
len = 0;
block = 0;
}
/* we don't write out susp fields for the root node */
if (t->rockridge) {
if (file_id == 0) {
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.self_susp, &buf[len_dr]);
len_dr += node->info.dir.self_susp.non_CE_len;
} else if (file_id == 1) {
assert(node->type == ECMA119_DIR);
susp_write(t, &node->info.dir.parent_susp, &buf[len_dr]);
len_dr += node->info.dir.parent_susp.non_CE_len;
} else if (file_id < 0) {
susp_write(t, &node->susp, &buf[len_dr]);
len_dr += node->susp.non_CE_len;
}
}
if (file_id == 1 && node->parent)
node = node->parent;
rec->len_dr[0] = len_dr;
iso_bb(rec->block, block, 4);
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
rec->len_fi[0] = len_fi;
memcpy(rec->file_id, name, len_fi);
}
static void
write_one_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *dir,
uint8_t *buf)
{
size_t i;
int j;
size_t len;
uint8_t *orig_buf = buf;
uint8_t *prior_buf = buf;
assert(dir->type == ECMA119_DIR);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
write_one_dir_record(t, dir, 1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
for (i = 0; i < dir->info.dir.nchildren; i++) {
write_one_dir_record(t, dir->info.dir.children[i], -1, buf);
len = ((struct ecma119_dir_record*) buf)->len_dr[0];
if ((buf + len - prior_buf) >= 2048) {
for (j = len - 1; j >= 0; j--) {
prior_buf[2048 + j] = buf[j];
buf[j] = 0;
}
prior_buf += 2048;
buf = prior_buf + len;
}
else {
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
}
}
/* write the susp continuation areas */
if (t->rockridge) {
susp_write_CE(t, &dir->info.dir.self_susp, buf);
buf += dir->info.dir.self_susp.CE_len;
susp_write_CE(t, &dir->info.dir.parent_susp, buf);
buf += dir->info.dir.parent_susp.CE_len;
for (i = 0; i < dir->info.dir.nchildren; i++) {
susp_write_CE(t, &dir->info.dir.children[i]->susp, buf);
buf += dir->info.dir.children[i]->susp.CE_len;
}
}
assert (buf - orig_buf == dir->info.dir.len + dir->info.dir.CE_len);
}
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
struct ecma119_tree_node *dir;
for (i = 0; i < t->dirlist_len; i++) {
dir = t->dirlist[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->info.dir.len + dir->info.dir.CE_len, t->block_size);
}
}
void
ecma119_start_chunking(struct ecma119_write_target *t,
write_fn writer,
off_t data_size,
uint8_t *buf)
{
if (data_size != t->state_data_size) {
data_size = round_up(data_size, t->block_size);
t->state_data = realloc(t->state_data, data_size);
t->state_data_size = data_size;
}
memset(t->state_data, 0, t->state_data_size);
t->state_data_off = 0;
t->state_data_valid = 1;
writer(t, t->state_data);
write_data_chunk(t, buf);
}
static void
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
{
memcpy(buf, t->state_data + t->state_data_off, t->block_size);
t->state_data_off += t->block_size;
if (t->state_data_off >= t->state_data_size) {
assert (t->state_data_off <= t->state_data_size);
t->state_data_valid = 0;
next_state(t);
}
}
static int
bs_read(struct burn_source *bs, unsigned char *buf, int size)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
if (size != t->block_size) {
warnx("you must read data in block-sized chunks (%d bytes)",
(int)t->block_size);
return 0;
} else if (t->curblock >= t->vol_space_size) {
return 0;
}
if (t->state_data_valid)
write_data_chunk(t, buf);
else
writers[t->state](t, buf);
t->curblock++;
return size;
}
static off_t
bs_get_size(struct burn_source *bs)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
return t->total_size;
}
static void
bs_free_data(struct burn_source *bs)
{
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
ecma119_tree_free(t->root);
iso_file_table_clear(t->file_table);
free(t->dirlist);
free(t->pathlist);
free(t->dirlist_joliet);
free(t->pathlist_joliet);
free(t->filelist);
free(t->state_data);
if (t->joliet)
joliet_tree_free(t->joliet_root);
if (t->state_files.fd)
fclose(t->state_files.fd);
}
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
struct ecma119_source_opts *opts)
{
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
ret->refcount = 1;
ret->read = bs_read;
ret->get_size = bs_get_size;
ret->free_data = bs_free_data;
ret->data = ecma119_target_new(volset, opts);
return ret;
}

View File

@ -0,0 +1,306 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file ecma119.h
*
* Structures and definitions used for writing an emca119 (ISO9660) compatible
* volume.
*/
#ifndef LIBISO_ECMA119_H
#define LIBISO_ECMA119_H
#include <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;
int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
struct el_torito_boot_catalog *catalog;
int replace_mode; /**< Replace ownership and modes of files
*
* 0. filesystem values
* 1. useful values
* bits 1-4 bitmask:
* 2 - replace dir
* 3 - replace file
* 4 - replace gid
* 5 - replace uid
*/
mode_t dir_mode;
mode_t file_mode;
gid_t gid;
uid_t uid;
char *input_charset;
char *ouput_charset;
int cache_inodes;
int sort_files; /**< if sort files or not. Sorting is based of
* the weight of each file */
/**
* In the CD, each file must have an unique inode number. So each
* time we add a new file, this is incremented.
*/
ino_t ino;
int curblock;
uint16_t block_size;
uint32_t path_table_size;
uint32_t path_table_size_joliet;
uint32_t l_path_table_pos;
uint32_t m_path_table_pos;
uint32_t l_path_table_pos_joliet;
uint32_t m_path_table_pos_joliet;
uint32_t total_dir_size;
uint32_t total_dir_size_joliet;
struct ecma119_tree_node **dirlist;
/**< A pre-order list of directories
* (this is the order in which we write
* out directory records).
*/
struct ecma119_tree_node **pathlist;
/**< A breadth-first list of
* directories. This is used for
* writing out the path tables.
*/
size_t dirlist_len; /**< The length of the previous 2 lists.
*/
struct iso_file_table *file_table;
/**<
* A hash table with info about all files
*/
struct iso_file **filelist; /**< A pre-order list of files.*/
size_t filelist_len; /* Length of the previous list. */
int curfile; /**< Used as a helper field for writing
out filelist and dirlist */
/* Joliet versions of the above lists. Since Joliet doesn't require
* directory relocation, the order of these lists might be different
* from the lists above (but they will be the same length).
*/
struct joliet_tree_node **dirlist_joliet;
struct joliet_tree_node **pathlist_joliet;
size_t dirlist_len_joliet;
enum ecma119_write_state state; /* The current state of the writer. */
/* Most writers work by
* 1) making sure state_data is big enough for their data
* 2) writing _all_ their data into state_data
* 3) relying on write_data_chunk to write the data block
* by block.
*/
uint8_t *state_data;
off_t state_data_size;
off_t state_data_off;
int state_data_valid;
/* for writing out files */
struct state_files {
off_t pos; /* The number of bytes we have written
* so far in the current file.
*/
off_t data_len;/* The number of bytes in the currently
* open file.
*/
FILE *fd; /* The currently open file. */
int file; /* The index in filelist that we are
* currently writing (or about to write). */
} state_files;
};
#define BP(a,b) [(b) - (a) + 1]
struct ecma119_pri_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t unused1 BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t unused3 BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
struct ecma119_sup_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t vol_flags BP(8, 8);
uint8_t system_id BP(9, 40);
uint8_t volume_id BP(41, 72);
uint8_t unused2 BP(73, 80);
uint8_t vol_space_size BP(81, 88);
uint8_t esc_sequences BP(89, 120);
uint8_t vol_set_size BP(121, 124);
uint8_t vol_seq_number BP(125, 128);
uint8_t block_size BP(129, 132);
uint8_t path_table_size BP(133, 140);
uint8_t l_path_table_pos BP(141, 144);
uint8_t opt_l_path_table_pos BP(145, 148);
uint8_t m_path_table_pos BP(149, 152);
uint8_t opt_m_path_table_pos BP(153, 156);
uint8_t root_dir_record BP(157, 190);
uint8_t vol_set_id BP(191, 318);
uint8_t publisher_id BP(319, 446);
uint8_t data_prep_id BP(447, 574);
uint8_t application_id BP(575, 702);
uint8_t copyright_file_id BP(703, 739);
uint8_t abstract_file_id BP(740, 776);
uint8_t bibliographic_file_id BP(777, 813);
uint8_t vol_creation_time BP(814, 830);
uint8_t vol_modification_time BP(831, 847);
uint8_t vol_expiration_time BP(848, 864);
uint8_t vol_effective_time BP(865, 881);
uint8_t file_structure_version BP(882, 882);
uint8_t reserved1 BP(883, 883);
uint8_t app_use BP(884, 1395);
uint8_t reserved2 BP(1396, 2048);
};
struct ecma119_boot_rec_vol_desc
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t boot_sys_id BP(8, 39);
uint8_t boot_id BP(40, 71);
uint8_t boot_catalog BP(72, 75);
uint8_t unused BP(76, 2048);
};
struct ecma119_vol_desc_terminator
{
uint8_t vol_desc_type BP(1, 1);
uint8_t std_identifier BP(2, 6);
uint8_t vol_desc_version BP(7, 7);
uint8_t reserved BP(8, 2048);
};
struct ecma119_dir_record
{
uint8_t len_dr BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 10);
uint8_t length BP(11, 18);
uint8_t recording_time BP(19, 25);
uint8_t flags BP(26, 26);
uint8_t file_unit_size BP(27, 27);
uint8_t interleave_gap_size BP(28, 28);
uint8_t vol_seq_number BP(29, 32);
uint8_t len_fi BP(33, 33);
uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
/* padding field (if len_fi is even) */
/* system use (len_dr - len_su + 1 to len_dr) */
};
struct ecma119_path_table_record
{
uint8_t len_di BP(1, 1);
uint8_t len_xa BP(2, 2);
uint8_t block BP(3, 6);
uint8_t parent BP(7, 8);
uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
/* padding field (if len_di is odd) */
};
/**
* A utility function for writers that want to write their data all at once
* rather than block-by-block. This creates a buffer of size \p size, passes
* it to the given writer, then hands out block-sized chunks.
*/
void
ecma119_start_chunking(struct ecma119_write_target *t,
void (*)(struct ecma119_write_target*, uint8_t*),
off_t size,
uint8_t *buf);
#endif /* LIBISO_ECMA119_H */

View File

@ -0,0 +1,623 @@
/* 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(iso);
iso_file_table_add_file(t->file_table, file);
file->ino = ++t->ino;
} else {
/* increment number of hard-links */
file->nlink++;
}
ret->attrib.st_nlink = file->nlink;
ret->attrib.st_ino = file->ino;
ret->info.file = file;
return ret;
}
/**
* Create a new ECMA-119 node representing a placeholder for a relocated
* dir.
*
* See IEEE P1282, section 4.1.5 for details
*/
static struct ecma119_tree_node*
create_placeholder(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct ecma119_tree_node *real)
{
struct ecma119_tree_node *ret;
assert(t && real && real->type == ECMA119_DIR
&& parent && parent->type == ECMA119_DIR);
ret = calloc(1, sizeof(struct ecma119_tree_node));
ret->iso_name = real->iso_name; /* TODO strdup? */
/* FIXME
* if we strdup above, if name changes in mangle_all,
* this probably keeps as original.
* if not, both change, but we need to update dirent_len.
* I think that attributes of a placeholder must be taken from
* real_me, not keept here.
* FIXME
* Another question is that real is a dir, while placeholder is
* a file, and ISO name restricctions are different, what to do?
*/
ret->dirent_len = real->dirent_len;
ret->attrib = real->attrib;
ret->full_name = strdup(real->full_name);
ret->target = t;
ret->parent = parent;
ret->type = ECMA119_PLACEHOLDER;
ret->info.real_me = real;
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
/**
* Create a new ECMA-119 node representing a symbolic link from a iso symlink
* node.
*/
static struct ecma119_tree_node*
create_symlink(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node_symlink *iso)
{
struct ecma119_tree_node *ret;
assert(t && iso && parent && parent->type == ECMA119_DIR);
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
ret->iso_name = iso->node.name ? ((t->iso_level == 1) ?
iso_1_fileid(iso->node.name, t->input_charset)
: iso_2_fileid(iso->node.name, t->input_charset))
: NULL;
ret->type = ECMA119_SYMLINK;
ret->info.dest = iso->dest; /* TODO strdup? */
ret->attrib.st_nlink = 1;
ret->attrib.st_ino = ++t->ino;
return ret;
}
/**
* Create a new ECMA-119 node representing a boot catalog. This is like a
* regular file, but its contents are taken from a El-Torito boot catalog,
* and not from a file in the local filesystem.
*
* See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for
* more details.
*/
static struct ecma119_tree_node*
create_boot_catalog(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node_boot_catalog *iso)
{
struct ecma119_tree_node *ret;
struct iso_file *file;
assert(t && iso && parent && parent->type == ECMA119_DIR);
/*
* This will simply create a ECMA119 file, with the only difference
* that the iso_file is not taken from table, but from boot catalog
*/
ret = create_ecma119_node(t, parent, (struct iso_tree_node*) iso);
ret->type = ECMA119_FILE;
file = iso->catalog->file;
file->ino = ++t->ino;
ret->attrib.st_nlink = file->nlink;
ret->attrib.st_ino = file->ino;
ret->info.file = file;
return ret;
}
/**
* Create a new ECMA-119 node that corresponds to the given iso tree node.
* If that node is a dir, this function recurses over all their children,
* thus creating a ECMA-119 tree whose root is the given iso dir.
*/
static struct ecma119_tree_node*
create_tree(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
struct iso_tree_node *iso)
{
struct ecma119_tree_node *ret = NULL;
assert(t && iso);
if ( iso->hide_flags & LIBISO_HIDE_ON_RR )
return NULL;
switch ( iso->type ) {
case LIBISO_NODE_FILE:
ret = create_file(t, parent, (struct iso_tree_node_file*)iso);
break;
case LIBISO_NODE_SYMLINK:
if ( !t->rockridge )
printf("Can't add symlinks to a non ISO tree. Skipping %s \n",
iso->name);
else
ret = create_symlink(t, parent, (struct iso_tree_node_symlink*)iso);
break;
case LIBISO_NODE_DIR:
{
size_t i;
struct iso_tree_node_dir *dir = (struct iso_tree_node_dir*)iso;
ret = create_dir(t, parent, dir);
for (i = 0; i < dir->nchildren; i++) {
struct ecma119_tree_node *child;
child = create_tree(t, ret, dir->children[i]);
if (child)
ret->info.dir.children[ret->info.dir.nchildren++] = child;
}
}
break;
case LIBISO_NODE_BOOTCATALOG:
ret = create_boot_catalog(t, parent,
(struct iso_tree_node_boot_catalog*)iso);
break;
default:
/* should never happen */
assert( 0 );
break;
}
return ret;
}
void
ecma119_tree_free(struct ecma119_tree_node *root)
{
size_t i;
if (root->type == ECMA119_DIR) {
for (i=0; i < root->info.dir.nchildren; i++) {
ecma119_tree_free(root->info.dir.children[i]);
}
free(root->info.dir.children);
}
free(root->iso_name);
free(root->full_name);
free(root);
}
static size_t
max_child_name_len(struct ecma119_tree_node *root)
{
size_t ret = 0, i;
assert(root->type == ECMA119_DIR);
for (i=0; i < root->info.dir.nchildren; i++) {
size_t len = strlen(root->info.dir.children[i]->iso_name);
ret = MAX(ret, len);
}
return ret;
}
/**
* Relocates a directory, as specified in Rock Ridge Specification
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels
* on a directory hierarchy exceeds 8, or the length of a path is higher
* than 255 characters, as specified in ECMA-119, section 6.8.2.1
*/
static void
reparent(struct ecma119_tree_node *child,
struct ecma119_tree_node *parent)
{
int found = 0;
size_t i;
struct ecma119_tree_node *placeholder;
assert(child && parent && parent->type == ECMA119_DIR && child->parent);
/* replace the child in the original parent with a placeholder */
for (i=0; i < child->parent->info.dir.nchildren; i++) {
if (child->parent->info.dir.children[i] == child) {
placeholder = create_placeholder(child->target,
child->parent, child);
child->parent->info.dir.children[i] = placeholder;
found = 1;
break;
}
}
assert(found);
/* add the child to its new parent */
child->parent = parent;
parent->info.dir.nchildren++;
parent->info.dir.children = realloc( parent->info.dir.children,
sizeof(void*) * parent->info.dir.nchildren );
parent->info.dir.children[parent->info.dir.nchildren-1] = child;
}
/**
* Reorder the tree, if necessary, to ensure that
* - the depth is at most 8
* - each path length is at most 255 characters
* This restriction is imposed by ECMA-119 specification (see ECMA-119,
* 6.8.2.1).
*/
static void
reorder_tree(struct ecma119_write_target *t,
struct ecma119_tree_node *root,
struct ecma119_tree_node *cur)
{
size_t max_path;
assert(root && cur && cur->type == ECMA119_DIR);
cur->info.dir.depth = cur->parent ? cur->parent->info.dir.depth + 1 : 1;
cur->info.dir.path_len = cur->parent ? cur->parent->info.dir.path_len
+ strlen(cur->iso_name) : 0;
max_path = cur->info.dir.path_len + cur->info.dir.depth
+ max_child_name_len(cur);
if (cur->info.dir.depth > 8 || max_path > 255) {
if (t->rockridge) {
reparent(cur, root);
/* we are appended to the root's children now, so there is no
* need to recurse (the root will hit us again) */
} else {
/* we need to delete cur */
size_t i,j;
struct ecma119_tree_node *parent = cur->parent;
printf("Can't dirs deeper than 8 without RR. Skipping %s\n",
cur->full_name);
for (i=0; i < parent->info.dir.nchildren; ++i) {
if (parent->info.dir.children[i] == cur) {
break;
}
}
assert ( i < parent->info.dir.nchildren);
for ( j = i; j < parent->info.dir.nchildren - 1; ++j)
parent->info.dir.children[j] = parent->info.dir.children[j+1];
parent->info.dir.nchildren--;
ecma119_tree_free(cur);
}
} else {
size_t i;
for (i=0; i < cur->info.dir.nchildren; i++) {
if (cur->info.dir.children[i]->type == ECMA119_DIR)
reorder_tree(t, root, cur->info.dir.children[i]);
}
}
}
/**
* Compare the iso name of two ECMA-119 nodes
*/
static int
cmp_node(const void *f1, const void *f2)
{
struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
return strcmp(f->iso_name, g->iso_name);
}
/**
* Sorts a the children of each directory in the ECMA-119 tree represented
* by \p root, acording to the order specified in ECMA-119, section 9.3.
*/
static void
sort_tree(struct ecma119_tree_node *root)
{
size_t i;
assert(root && root->type == ECMA119_DIR);
qsort(root->info.dir.children, root->info.dir.nchildren,
sizeof(void*), cmp_node);
for (i=0; i < root->info.dir.nchildren; i++) {
if (root->info.dir.children[i]->type == ECMA119_DIR)
sort_tree(root->info.dir.children[i]);
}
}
/**
* Change num_change characters of the given filename in order to ensure the
* name is unique. If the name is short enough (depending on the ISO level),
* we can append the characters instead of changing them.
*
* \p seq_num is the index of this file in the sequence of identical filenames.
*
* For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
* "HELLOTHE03.TXT"
*/
static void
mangle_name(char **name, int num_change, int level, int seq_num)
{
char *dot = strrchr(*name, '.');
char *semi = strrchr(*name, ';');
size_t len = strlen(*name);
char base[len+1], ext[len+1];
char fmt[12];
size_t baselen, extlen;
if (num_change >= len) {
return;
}
strncpy(base, *name, len+1);
if (dot) {
base[dot - *name] = '\0';
strncpy(ext, dot+1, len+1);
if (semi) {
ext[semi - dot - 1] = '\0';
}
} else {
base[len-2] = '\0';
ext[0] = '\0';
}
baselen = strlen(base);
extlen = strlen(ext);
if (level == 1 && baselen + num_change > 8) {
base[8 - num_change] = '\0';
} else if (level != 1 && baselen + extlen + num_change > 30) {
base[30 - extlen - num_change] = '\0';
}
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
*name = realloc(*name, baselen + extlen + num_change + 4);
sprintf(*name, fmt, base, seq_num, ext);
}
/**
* Ensures that the ISO name of each children of the given dir is unique,
* changing some of them if needed.
*/
static void
mangle_all(struct ecma119_tree_node *dir)
{
size_t i, j, k;
struct ecma119_dir_info d = dir->info.dir;
size_t n_change;
int changed;
assert(dir->type == ECMA119_DIR);
do {
changed = 0;
for (i=0; i < d.nchildren; i++) {
/* find the number of consecutive equal names */
j = 1;
while ( i+j < d.nchildren &&
!strcmp(d.children[i]->iso_name,
d.children[i+j]->iso_name) )
j++;
if (j == 1) continue;
/* mangle the names */
changed = 1;
n_change = j / 10 + 1;
for (k=0; k < j; k++) {
mangle_name(&(d.children[i+k]->iso_name),
n_change,
dir->target->iso_level,
k);
d.children[i+k]->dirent_len =
calc_dirent_len(d.children[i+k]);
}
/* skip ahead by the number of mangled names */
i += j - 1;
}
} while (changed);
for (i=0; i < d.nchildren; i++) {
if (d.children[i]->type == ECMA119_DIR)
mangle_all(d.children[i]);
}
}
struct ecma119_tree_node*
ecma119_tree_create(struct ecma119_write_target *t,
struct iso_tree_node *iso_root)
{
t->root = create_tree(t, NULL, iso_root);
if ( !(t->relaxed_constraints & ECMA119_NO_DIR_REALOCATION) )
reorder_tree(t, t->root, t->root);
sort_tree(t->root);
mangle_all(t->root);
return t->root;
}
void
ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
{
size_t i;
char sp[spaces+1];
memset(sp, ' ', spaces);
sp[spaces] = '\0';
printf("%s%s\n", sp, root->iso_name);
if (root->type == ECMA119_DIR)
for (i=0; i < root->info.dir.nchildren; i++)
ecma119_tree_print(root->info.dir.children[i], spaces+2);
}

View File

@ -0,0 +1,98 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file ecma119_tree.h
*
* Declarations for creating, modifying and printing filesystem trees that
* are compatible with ecma119.
*/
#ifndef LIBISO_ECMA119_TREE_H
#define LIBISO_ECMA119_TREE_H
#include <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. */
};
struct ecma119_dir_info {
struct susp_info self_susp; /**< susp entries for "." */
struct susp_info parent_susp; /**< susp entries for ".." */
size_t len; /**< sum of the lengths of children's
* Directory Records (including SU) */
size_t CE_len; /**< sum of the lengths of children's
* SUSP CE areas */
size_t block;
int depth;
size_t path_len; /**< The length of a path up to, and
* including, this directory. This
* cannot exceed 255. */
size_t nchildren;
struct ecma119_tree_node **children;
struct ecma119_tree_node *real_parent;
/**< The parent before relocation */
};
/**
* A node for a tree containing all the information necessary for writing
* an ISO9660 volume.
*/
struct ecma119_tree_node
{
char *iso_name; /**< in ASCII, conforming to the
* current ISO level. */
char *full_name; /**< full name, in current locale (TODO put this in UTF-8?) */
size_t dirent_len; /**< Length of the directory record,
* not including SU. */
struct ecma119_tree_node *parent;
struct ecma119_write_target *target;
struct stat attrib;
struct susp_info susp;
enum ecma119_node_type type; /**< file, symlink, directory or placeholder */
union {
struct iso_file *file;
char *dest;
struct ecma119_dir_info dir;
struct ecma119_tree_node *real_me; /**< this field points to
* the relocated directory.
*/
} info;
};
/**
* Create a new ecma119_tree that corresponds to the tree represented by
* \p iso_root.
*/
struct ecma119_tree_node*
ecma119_tree_create(struct ecma119_write_target *target,
struct iso_tree_node *iso_root);
/**
* Free an ecma119 tree.
*/
void
ecma119_tree_free(struct ecma119_tree_node *root);
/**
* Print an ecma119 tree.
*/
void
ecma119_tree_print(struct ecma119_tree_node *root, int spaces);
#endif /* LIBISO_ECMA119_TREE_H */

View File

@ -0,0 +1,454 @@
#include "libisofs.h"
#include "eltorito.h"
#include "volume.h"
#include "util.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>
struct el_torito_validation_entry {
uint8_t header_id BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t reserved BP(3, 4);
uint8_t id_string BP(5, 28);
uint8_t checksum BP(29, 30);
uint8_t key_byte1 BP(31, 31);
uint8_t key_byte2 BP(32, 32);
};
struct el_torito_default_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t unused2 BP(13, 32);
};
struct el_torito_section_header_entry {
uint8_t header_indicator BP(1, 1);
uint8_t platform_id BP(2, 2);
uint8_t number BP(3, 4);
uint8_t character BP(5, 32);
};
struct el_torito_section_entry {
uint8_t boot_indicator BP(1, 1);
uint8_t boot_media_type BP(2, 2);
uint8_t load_seg BP(3, 4);
uint8_t system_type BP(5, 5);
uint8_t unused1 BP(6, 6);
uint8_t sec_count BP(7, 8);
uint8_t block BP(9, 12);
uint8_t selec_criteria BP(13, 13);
uint8_t vendor_sc BP(14, 32);
};
/**
* This table should be written with accuracy values at offset
* 8 of boot image, when used ISOLINUX boot loader
*/
struct boot_info_table {
uint8_t bi_pvd BP(1, 4); /* LBA of primary volume descriptor */
uint8_t bi_file BP(5, 8); /* LBA of boot file */
uint8_t bi_length BP(9, 12); /* Length of boot file */
uint8_t bi_csum BP(13, 16); /* Checksum of boot file */
uint8_t bi_reserved BP(17, 56); /* Reserved */
};
struct partition_desc {
uint8_t boot_ind;
uint8_t begin_chs[3];
uint8_t type;
uint8_t end_chs[3];
uint8_t start[4];
uint8_t size[4];
};
struct hard_disc_mbr {
uint8_t code_area[440];
uint8_t opt_disk_sg[4];
uint8_t pad[2];
struct partition_desc partition[4];
uint8_t sign1;
uint8_t sign2;
};
static struct el_torito_boot_image *
create_image(struct iso_tree_node *image,
enum eltorito_boot_media_type type)
{
struct el_torito_boot_image *boot;
int boot_media_type = 0;
int load_sectors = 0; /* number of sector to load */
unsigned char partition_type = 0;
switch (type) {
case ELTORITO_FLOPPY_EMUL:
switch (image->attrib.st_size) {
case 1200 * 1024:
boot_media_type = 1; /* 1.2 meg diskette */
break;
case 1440 * 1024:
boot_media_type = 2; /* 1.44 meg diskette */
break;
case 2880 * 1024:
boot_media_type = 3; /* 2.88 meg diskette */
break;
default:
fprintf(stderr, "Invalid image size %d Kb. Must be one of 1.2, 1.44"
"or 2.88 Mb", (int) image->attrib.st_size / 1024);
libisofs_errno = ELTORITO_WRONG_IMAGE_SIZE;
return NULL;
break;
}
/* it seems that for floppy emulation we need to load
* a single sector (512b) */
load_sectors = 1;
break;
case ELTORITO_HARD_DISC_EMUL:
{
size_t i;
int fd;
struct hard_disc_mbr mbr;
int used_partition;
/* read the MBR on disc and get the type of the partition */
fd = open(((struct iso_tree_node_file*)image)->path, O_RDONLY);
if ( fd == -1 ) {
fprintf(stderr, "Can't open image file\n");
return NULL;
}
if ( read(fd, &mbr, sizeof(mbr)) ) {
fprintf(stderr, "Can't read MBR from image file\n");
close(fd);
return NULL;
}
close(fd);
/* check valid MBR signature */
if ( mbr.sign1 != 0x55 || mbr.sign2 != 0xAA ) {
fprintf(stderr, "Invalid MBR. Wrong signature.\n");
return NULL;
}
/* ensure single partition */
used_partition = -1;
for (i = 0; i < 4; ++i) {
if (mbr.partition[i].type != 0) {
/* it's an used partition */
if (used_partition != -1) {
fprintf(stderr, "Invalid MBR. At least 2 paritions: %d and "
"%d, are being used\n", used_partition, i);
return NULL;
} else
used_partition = i;
}
}
partition_type = mbr.partition[used_partition].type;
}
boot_media_type = 4;
/* only load the MBR */
load_sectors = 1;
break;
case ELTORITO_NO_EMUL:
boot_media_type = 0;
break;
}
boot = calloc(1, sizeof(struct el_torito_boot_image));
boot->bootable = 1;
boot->image = (struct iso_tree_node_file *) image;
boot->type = boot_media_type;
boot->load_size = load_sectors;
boot->partition_type = partition_type;
return boot;
}
static struct iso_tree_node_boot_catalog*
create_boot_catalog_node(struct iso_tree_node_dir *parent,
const char *name)
{
struct iso_tree_node_boot_catalog *boot;
assert( parent && name );
boot = calloc(1, sizeof(struct iso_tree_node_boot_catalog));
boot->node.attrib.st_mode = S_IFREG | 0444;
boot->node.attrib.st_atime =
boot->node.attrib.st_mtime =
boot->node.attrib.st_ctime = time(NULL);
boot->node.type = LIBISO_NODE_BOOTCATALOG;
boot->node.name = strdup(name);
iso_tree_add_child(parent, (struct iso_tree_node*) boot);
return boot;
}
struct el_torito_boot_image *
iso_volume_create_boot_catalog(struct iso_volume *volume,
struct iso_tree_node *image,
enum eltorito_boot_media_type type,
struct iso_tree_node_dir *dir,
char *name)
{
struct el_torito_boot_image *boot_image;
struct iso_tree_node_boot_catalog *boot_node;
struct el_torito_boot_catalog *catalog;
assert( volume && !volume->bootcat);
assert( image && ISO_ISREG(image) && dir && name);
boot_image = create_image(image, type);
if ( !boot_image )
return NULL;
/* creates the catalog with the given default image */
catalog = malloc(sizeof(struct el_torito_boot_catalog));
catalog->nentries = 1;
catalog->entries = malloc(sizeof(struct el_torito_boot_image *));
catalog->entries[0] = boot_image;
catalog->file = malloc(sizeof(struct iso_file));
catalog->file->size = 2048;
/* add catalog file */
boot_node = create_boot_catalog_node(dir, name);
boot_node->catalog = catalog;
volume->bootcat = catalog;
return boot_image;
}
void
el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_seg = segment;
}
void
el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors)
{
if (bootimg->type != ELTORITO_NO_EMUL)
return;
bootimg->load_size = sectors;
}
void
el_torito_set_no_bootable(struct el_torito_boot_image *bootimg)
{
bootimg->bootable = 0;
}
void
el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg)
{
bootimg->patch_isolinux = 1;
}
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat)
{
size_t i;
assert(cat);
for(i = 0; i < cat->nentries; ++i) {
free(cat->entries[i]);
}
free(cat->entries);
free(cat->file);
free(cat);
}
void el_torito_get_image_files(struct ecma119_write_target *t)
{
size_t i;
struct el_torito_boot_catalog *cat = t->catalog;
assert(cat);
for(i = 0; i < cat->nentries; ++i) {
struct iso_tree_node_file *image = cat->entries[i]->image;
struct iso_file *file = iso_file_table_lookup(t->file_table, image);
if ( file == NULL ) {
file = iso_file_new(image);
iso_file_table_add_file(t->file_table, file);
}
cat->entries[i]->file = file;
}
}
/**
* Write the Boot Record Volume Descriptor
*/
static void
write_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct el_torito_boot_catalog *cat = t->catalog;
struct ecma119_boot_rec_vol_desc *vol =
(struct ecma119_boot_rec_vol_desc*)buf;
assert(cat);
vol->vol_desc_type[0] = 0;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
memcpy(vol->boot_sys_id, "EL TORITO SPECIFICATION", 23);
iso_lsb(vol->boot_catalog, cat->file->block, 4);
}
static void
write_validation_entry(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
int checksum;
struct el_torito_validation_entry *ve =
(struct el_torito_validation_entry*)buf;
ve->header_id[0] = 1;
ve->platform_id[0] = 0; /* 0: 80x86, 1: PowerPC, 2: Mac */
ve->key_byte1[0] = 0x55;
ve->key_byte2[0] = 0xAA;
/* calculate the checksum, to ensure sum of all words is 0 */
checksum = 0;
for (i = 0; i < sizeof(struct el_torito_validation_entry); i += 2) {
checksum -= buf[i];
checksum -= (buf[i] << 8);
}
iso_lsb(ve->checksum, checksum, 2);
}
static void
patch_boot_file(struct el_torito_boot_image *img)
{
struct boot_info_table info;
int fd;
uint32_t checksum;
ssize_t len;
uint8_t buf[4];
memset(&info, 0, sizeof(info));
/* open image */
fd = open(img->image->path, O_RDWR);
if ( fd == -1 ) {
//TODO what do do? exit or just continue?
fprintf(stderr, "Can't patch boot image %s\n", img->image->path);
close(fd);
return;
}
/* compute checksum, as the the sum of all 32 bit words in boot image
* from offset 64 */
checksum = 0;
lseek(fd, (off_t) 64, SEEK_SET);
//TODO this can (must) be optimizied by reading to a longer buffer
while ( (len = read(fd, buf, 4) ) == 4 ) {
checksum += iso_read_lsb(buf, 4);
}
if ( len != 0 ) {
/* error reading file, or file length not multiple of 4 */
//TODO what do do? exit or just continue?
fprintf(stderr, "Can't patch boot image %s\n", img->image->path);
close(fd);
return;
}
/* fill boot info table */
iso_lsb(info.bi_pvd, 16, 4); //FIXME this should be changed when we implement ms
iso_lsb(info.bi_file, img->file->block, 4);
iso_lsb(info.bi_length, img->image->node.attrib.st_size, 4);
iso_lsb(info.bi_csum, checksum, 4);
/* patch file */
lseek(fd, (off_t) 8, SEEK_SET);
write(fd, &info, sizeof(info));
close(fd);
}
void
el_torito_patch_image_files(struct ecma119_write_target *t)
{
size_t i;
struct el_torito_boot_catalog *cat = t->catalog;
assert(cat);
for (i = 0; i < cat->nentries; ++i) {
struct el_torito_boot_image *img = cat->entries[i];
if ( img->patch_isolinux )
patch_boot_file(img);
}
}
/**
* Write one section entry.
* Currently this is used for both default and other entries since we
* put selection criteria no 0 (no sel. criteria)
*/
static void
write_section_entry(uint8_t *buf, struct el_torito_boot_image *img)
{
struct el_torito_section_entry *se =
(struct el_torito_section_entry*)buf;
se->boot_indicator[0] = img->bootable ? 0x88 : 0x00;
se->boot_media_type[0] = img->type;
iso_lsb(se->load_seg, img->load_seg, 2);
se->system_type[0] = 0; //TODO need to get the partition type
iso_lsb(se->sec_count, img->load_size, 2);
iso_lsb(se->block, img->file->block, 4);
}
/**
* Write El-Torito Boot Catalog
*/
static void
write_catalog(struct ecma119_write_target *t, uint8_t *buf)
{
struct el_torito_boot_catalog *cat = t->catalog;
assert(cat);
assert(cat->nentries >= 1 && cat->nentries < 63);
write_validation_entry(t, buf);
/* write default entry */
write_section_entry(buf + 32, cat->entries[0]);
//TODO write all images
}
void
el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
assert(t->catalog);
ecma119_start_chunking(t,
write_boot_vol_desc,
2048,
buf);
}
void
el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf)
{
assert(t->catalog);
ecma119_start_chunking(t,
write_catalog,
2048,
buf);
}

View File

@ -0,0 +1,60 @@
#ifndef ELTORITO_H_
#define ELTORITO_H_
#include "tree.h"
#include "file.h"
#include "ecma119.h"
/**
* Location of the boot catalog
*/
struct iso_tree_node_boot_catalog
{
struct iso_tree_node node;
struct el_torito_boot_catalog *catalog;
};
struct el_torito_boot_catalog {
int nentries;
struct el_torito_boot_image **entries;
struct iso_file *file; /**< The catalog file */
};
struct el_torito_boot_image {
unsigned char bootable; /**< If the entry is bootable. */
unsigned char patch_isolinux; /**< If the image will be patched */
unsigned char type; /**< The type of image */
unsigned char partition_type; /**< type of partition for HD-emul images */
short load_seg; /**< Load segment for the initial boot image. */
short load_size; /**< Number of sector to load. */
struct iso_tree_node_file *image;
struct iso_file *file;
};
/*struct el_torito_boot_entry *
el_torito_add_boot_entry(struct el_torito_boot_catalog *cat,
struct iso_tree_node_file *image);
*/
void el_torito_boot_catalog_free(struct el_torito_boot_catalog *cat);
/**
* For each boot image file, this gets the related iso_file object.
* In most cases, the file is already in the hash table. However, if the
* boot record is hidden in both ISO/RR and joliet trees, this ensures
* that boot images will be written to image.
*/
void el_torito_get_image_files(struct ecma119_write_target *t);
/**
* Patch image files if selected. This is needed for isolinux boot images
*/
void el_torito_patch_image_files(struct ecma119_write_target *t);
void
el_torito_wr_boot_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
void
el_torito_wr_catalog(struct ecma119_write_target *t, uint8_t *buf);
#endif /*ELTORITO_H_*/

View File

@ -0,0 +1,38 @@
#include "exclude.h"
void
iso_exclude_add_path(struct iso_hash_table *table, const char *path)
{
if (!path)
return;
table->num += iso_hash_insert(table->table, path);
}
/*void
iso_exclude_remove_path(struct iso_hash_table *table, const char *path)
{
if (!table->num || !path)
return;
table->num -= iso_hash_remove(table->table, path);
}*/
void
iso_exclude_empty(struct iso_hash_table *table)
{
if (!table->num)
return;
iso_hash_empty(table->table);
table->num=0;
}
int
iso_exclude_lookup(struct iso_hash_table *table, const char *path)
{
if (!table->num || !path)
return 0;
return iso_hash_lookup(table->table, path);
}

View File

@ -0,0 +1,37 @@
#ifndef ISO_EXCLUDE_H
#define ISO_EXCLUDE_H
#include "hash.h"
struct iso_hash_table {
struct iso_hash_node *table[HASH_NODES];
int num;
};
/**
* Add a path to ignore when adding a directory recursively.
*
* \param path The path, on the local filesystem, of the file.
*/
int iso_exclude_lookup(struct iso_hash_table *table, const char *path);
/**
* Add the path of a file or directory to ignore when adding a directory recursively.
*
* \param path The path, on the local filesystem, of the file.
*/
void iso_exclude_add_path(struct iso_hash_table *table, const char *path);
/**
* Remove a path that was set to be ignored when adding a directory recusively.
*
* \param path The path, on the local filesystem, of the file.
*/
//void iso_exclude_remove_path(struct iso_hash_table *table, const char *path);
/**
* Remove all paths that were set to be ignored when adding a directory recusively.
*/
void iso_exclude_empty(struct iso_hash_table *table);
#endif /* ISO_EXCLUDE */

View File

@ -0,0 +1,188 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "file.h"
#include "tree.h"
//TODO: refactor both hash and this hash table into a single one!!
struct iso_file *
iso_file_new(struct iso_tree_node_file *f)
{
struct iso_file *file = calloc(1, sizeof(struct iso_file));
file->path = f->path; /*TODO strdup? it needs to be free on clear then */
file->size = f->node.attrib.st_size;
file->nlink = 1;
file->real_dev = f->node.attrib.st_dev;
file->real_ino = f->node.attrib.st_ino;
file->sort_weight = f->sort_weight;
return file;
}
static unsigned int
iso_file_table_hash(const char *path)
{
unsigned int hash_num=0;
const char *c;
c=path;
while(*c)
hash_num = (hash_num << 15) + (hash_num << 3) + (hash_num >> 3) + *c++;
return hash_num % FILE_HASH_NODES;
}
static inline unsigned int
iso_file_table_hash_inode(dev_t dev, ino_t ino)
{
return (dev ^ ino) % FILE_HASH_NODES;
}
struct iso_file_table*
iso_file_table_new(int cache_inodes)
{
struct iso_file_table *table = calloc(1, sizeof(struct iso_file_table));
table->cache_inodes = cache_inodes;
return table;
}
static struct iso_file_hash_node *
iso_file_table_node_new(struct iso_file *file)
{
struct iso_file_hash_node *node;
node = calloc(1, sizeof(struct iso_file_hash_node) );
node->file = file;
return node;
}
static void
iso_file_table_node_free(struct iso_file_hash_node *node)
{
free(node->file);
free(node);
}
void
iso_file_table_clear(struct iso_file_table *ft)
{
int i;
for (i=0; i < FILE_HASH_NODES; i++) {
struct iso_file_hash_node *node;
node=ft->table[i];
if (!node)
continue;
ft->table[i] = NULL;
do {
struct iso_file_hash_node *next;
next = node->next;
iso_file_table_node_free(node);
node = next;
} while (node);
}
ft->count = 0;
}
/**
* return 0 if equal, != 0 if not
*/
static int
iso_table_compare_files(struct iso_file_table *ft,
struct iso_file *f1, struct iso_file *f2)
{
if (ft->cache_inodes) {
return (f1->real_dev != f2->real_dev) || (f1->real_ino != f2->real_ino);
} else {
return strcmp(f1->path, f2->path);
}
}
int
iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f)
{
struct iso_file_hash_node *node;
unsigned int hash_num;
/* find the hash number */
if (ft->cache_inodes)
hash_num = iso_file_table_hash_inode(f->real_dev, f->real_ino);
else
hash_num = iso_file_table_hash(f->path);
/* insert it */
node = ft->table[hash_num];
/* unfortunately, we can't safely consider that a file
* won't be twice in the hash table so make sure it
* doesn't already exists */
if (!node) {
ft->table[hash_num]=iso_file_table_node_new(f);
ft->count++;
return 1;
}
/* if it's already in, we don't do anything */
if (!iso_table_compare_files(ft, f, node->file))
return 0;
while (node->next) {
node = node->next;
/* if it's already in, we don't do anything */
if (!iso_table_compare_files(ft, f, node->file))
return 0;
}
node->next = iso_file_table_node_new(f);
ft->count++;
return 1;
}
struct iso_file *
iso_file_table_lookup(struct iso_file_table *ft, struct iso_tree_node_file *f)
{
struct iso_file_hash_node *node;
unsigned int hash_num;
int equal;
/* find the hash number */
if ( ft->cache_inodes )
hash_num = iso_file_table_hash_inode(f->node.attrib.st_dev,
f->node.attrib.st_ino);
else
hash_num = iso_file_table_hash(f->path);
node = ft->table[hash_num];
if (!node)
return NULL;
equal = ft->cache_inodes ?
((f->node.attrib.st_dev == node->file->real_dev)
&& (f->node.attrib.st_ino == node->file->real_ino))
: !strcmp(f->path, node->file->path);
if (equal)
return node->file;
while (node->next) {
node = node->next;
equal = ft->cache_inodes ?
((f->node.attrib.st_dev == node->file->real_dev)
&& (f->node.attrib.st_ino == node->file->real_ino))
: !strcmp(f->path, node->file->path);
if (equal)
return node->file;
}
return NULL;
}

View File

@ -0,0 +1,69 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file file.h
*
* Declare the structs to keep track of the files to be written into image.
* These files are stored in a hash table. Two modes of operation are supported:
* when cache inodes is enabled, the files are indexed into the table by the
* device and inode id in the local filesystem. This way, two different files
* with same inode and device id are treated as if they were a single file.
* This is usually the correct behavior, as a different file with same inode
* and device used to be a hard link.
* When cache inode is disabled, indexing is done by path on local filesystem.
*/
#ifndef FILE_H_
#define FILE_H_
#define FILE_HASH_NODES 2048
struct iso_file {
char *path; /**< Path of the file on local filesystem */
off_t size; /**< size of this file */
ino_t ino; /**< This will be the inode number on CD of the file (RR) */
nlink_t nlink; /**< Number of hard links of the file on CD (RR) */
size_t block; /**< Block where this file is to be written on image */
dev_t real_dev;
ino_t real_ino; /**< for lookup by inode caching */
int sort_weight;
};
struct iso_file_hash_node {
struct iso_file_hash_node *next;
struct iso_file *file;
};
struct iso_file_table {
struct iso_file_hash_node *table[FILE_HASH_NODES];
size_t count;
int cache_inodes; /**< 1 to index by inode number */
};
struct iso_tree_node_file;
/**
* Create a struct that represents the specified iso_tree_node_file,
* suitable to be stored into the table,
*/
struct iso_file *iso_file_new(struct iso_tree_node_file*);
struct iso_file_table *iso_file_table_new(int cache_inodes);
/**
* Clear a hash table. All iso_file structs stored will also be freed,
* but not the path of each iso_file
*/
void iso_file_table_clear(struct iso_file_table *ft);
/**
* Add a new file to the table.
* \return 1 if the file is added, 0 if the file already exist on table
*/
int iso_file_table_add_file(struct iso_file_table *ft, struct iso_file *f);
struct iso_file *iso_file_table_lookup(struct iso_file_table *ft,
struct iso_tree_node_file *f);
#endif /*FILE_H_*/

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,464 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
#include "joliet.h"
#include "ecma119.h"
#include "ecma119_tree.h"
#include "tree.h"
#include "util.h"
#include "volume.h"
#include "eltorito.h"
#include <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(iso_f);
iso_file_table_add_file(t->file_table, file);
}
ret->info.file = file;
ret->type = JOLIET_FILE;
} else {
/* it's boot catalog info */
struct iso_tree_node_boot_catalog *iso_b =
(struct iso_tree_node_boot_catalog *) iso;
struct iso_file *file;
file = iso_b->catalog->file;
ret->info.file = file;
ret->type = JOLIET_FILE;
}
return ret;
}
static struct joliet_tree_node*
create_tree(struct ecma119_write_target *t,
struct joliet_tree_node *parent,
struct iso_tree_node *iso)
{
struct joliet_tree_node *root;
assert(t && iso);
if ( iso->hide_flags & LIBISO_HIDE_ON_JOLIET )
return NULL;
switch (iso->type) {
case LIBISO_NODE_FILE:
case LIBISO_NODE_BOOTCATALOG:
root = create_node(t, parent, iso);
break;
case LIBISO_NODE_DIR:
{
size_t i;
struct joliet_tree_node *node;
struct iso_tree_node_dir *dir;
root = create_node(t, parent, iso);
dir = (struct iso_tree_node_dir*)iso;
for (i = 0; i < dir->nchildren; ++i) {
node = create_tree(t, root, dir->children[i]);
if ( node != NULL ) {
root->info.dir.children[root->info.dir.nchildren++] = node;
}
}
}
break;
default:
//TODO replace this printf
printf("Can't add this kind of node to a Joliet tree");
return NULL;
break;
}
return root;
}
static int
cmp_node(const void *f1, const void *f2)
{
struct joliet_tree_node *f = *((struct joliet_tree_node**)f1);
struct joliet_tree_node *g = *((struct joliet_tree_node**)f2);
return ucscmp(f->name, g->name);
}
static void
sort_tree(struct joliet_tree_node *root)
{
size_t i;
assert(root && (root->type == JOLIET_DIR));
qsort(root->info.dir.children, root->info.dir.nchildren,
sizeof(void*), cmp_node);
for (i = 0; i < root->info.dir.nchildren; i++) {
struct joliet_tree_node *child = root->info.dir.children[i];
if ( child->type == JOLIET_DIR )
sort_tree(child);
}
}
void
joliet_prepare_path_tables(struct ecma119_write_target *t)
{
size_t cur, i, j;
t->pathlist_joliet[0] = t->joliet_root;
t->path_table_size_joliet = 10; /* root directory record */
cur = 1;
for (i = 0; i < t->dirlist_len_joliet; i++) {
struct joliet_tree_node *dir = t->pathlist_joliet[i];
for (j = 0; j < dir->info.dir.nchildren; j++) {
struct joliet_tree_node *ch = dir->info.dir.children[j];
if (ch->type == JOLIET_DIR) {
size_t len = 8 + ucslen(ch->name)*2;
t->pathlist_joliet[cur++] = ch;
t->path_table_size_joliet += len;
}
}
}
}
/**
* Calculate the size of each directory.
*/
void
joliet_calc_dir_size(struct ecma119_write_target *t,
struct joliet_tree_node *root)
{
size_t i;
size_t newlen;
struct joliet_tree_node *ch;
assert(root && (root->type == JOLIET_DIR) );
t->dirlist_len_joliet++;
root->info.dir.len = 68; /* for "." and ".." entries */
for (i = 0; i < root->info.dir.nchildren; ++i) {
ch = root->info.dir.children[i];
newlen = root->info.dir.len + ch->dirent_len;
if ((newlen % 2048) < (root->info.dir.len % 2048)) {
root->info.dir.len = newlen + (2048 - (root->info.dir.len % 2048));
} else {
root->info.dir.len += ch->dirent_len;
}
if (ch->type == JOLIET_DIR)
joliet_calc_dir_size(t, ch);
}
t->total_dir_size_joliet += round_up (root->info.dir.len, t->block_size);
}
/**
* Calculate the position of each directory. Also fill out t->dirlist_joliet.
*/
void
joliet_calc_dir_pos(struct ecma119_write_target *t,
struct joliet_tree_node *root)
{
size_t i;
struct joliet_tree_node *ch;
assert(root && (root->type == JOLIET_DIR));
root->info.dir.block = t->curblock;
t->curblock += div_up(root->info.dir.len, t->block_size);
t->dirlist_joliet[t->curfile++] = root;
for (i = 0; i < root->info.dir.nchildren; i++) {
ch = root->info.dir.children[i];
if (ch->type == JOLIET_DIR)
joliet_calc_dir_pos(t, ch);
}
}
struct joliet_tree_node*
joliet_tree_create(struct ecma119_write_target *t,
struct iso_tree_node *iso_root)
{
struct joliet_tree_node *root = create_tree(t, NULL, iso_root);
sort_tree(root);
return root;
}
void
joliet_tree_free(struct joliet_tree_node *root)
{
size_t i;
if (root->type == JOLIET_DIR) {
for (i=0; i < root->info.dir.nchildren; i++) {
joliet_tree_free(root->info.dir.children[i]);
}
free(root->info.dir.children);
}
free(root->name);
free(root);
}
/* ugh. this is mostly C&P */
static void
write_path_table(struct ecma119_write_target *t,
int l_type,
uint8_t *buf)
{
void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
iso_lsb : iso_msb;
size_t i;
struct ecma119_path_table_record *rec;
struct joliet_tree_node *dir;
int parent = 0;
assert (t->joliet);
for (i = 0; i < t->dirlist_len; i++) {
dir = t->pathlist_joliet[i];
while ((i) && t->pathlist_joliet[parent] != dir->parent)
parent++;
assert(parent < i || i == 0);
rec = (struct ecma119_path_table_record*) buf;
rec->len_di[0] = dir->parent ?
(uint8_t) ucslen(dir->name) * 2 : 1;
rec->len_xa[0] = 0;
write_int(rec->block, dir->info.dir.block, 4);
write_int(rec->parent, parent + 1, 2);
if (dir->parent)
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
}
}
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
* file_id == 3 means that we are writing the root directory record (in order
* to distinguish it from the "." entry in the root directory) */
static void
write_one_dir_record(struct ecma119_write_target *t,
struct joliet_tree_node *node,
int file_id,
uint8_t *buf)
{
uint32_t len;
uint32_t block;
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2;
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
if (node->type == JOLIET_DIR) {
len = node->info.dir.len;
block = node->info.dir.block;
} else {
/* file */
len = node->info.file->size;
block = node->info.file->block;
}
if (file_id == 1 && node->parent)
node = node->parent;
rec->len_dr[0] = len_dr;
iso_bb(rec->block, block, 4);
iso_bb(rec->length, len, 4);
iso_datetime_7(rec->recording_time, t->now);
rec->flags[0] = (node->type == JOLIET_DIR) ? 2 : 0;
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
rec->len_fi[0] = len_fi;
memcpy(rec->file_id, name, len_fi);
}
static void
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table (t, 1, buf);
}
static void
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
{
write_path_table (t, 0, buf);
}
static void
write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
{
struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf;
struct iso_volume *volume = t->volset->volume[t->volnum];
uint16_t *vol_id = str2ucs(volume->volume_id, t->input_charset);
uint16_t *pub_id = str2ucs(volume->publisher_id, t->input_charset);
uint16_t *data_id = str2ucs(volume->data_preparer_id, t->input_charset);
uint16_t *volset_id = str2ucs(t->volset->volset_id, t->input_charset);
int vol_id_len = MIN(32, ucslen(vol_id) * 2);
int pub_id_len = MIN(128, ucslen(pub_id) * 2);
int data_id_len = MIN(128, ucslen(data_id) * 2);
int volset_id_len = MIN(128, ucslen(volset_id) * 2);
uint16_t *system_id = str2ucs(volume->system_id, t->input_charset);
uint16_t *application_id = str2ucs(volume->application_id, t->input_charset);
uint16_t *copyright_file_id = str2ucs(volume->copyright_file_id, t->input_charset);
uint16_t *abstract_file_id = str2ucs(volume->abstract_file_id, t->input_charset);
uint16_t *biblio_file_id = str2ucs(volume->biblio_file_id, t->input_charset);
int system_id_len = MIN(32, ucslen(system_id) * 2);
int application_id_len = MIN(128, ucslen(application_id) * 2);
int copyright_file_id_len = MIN(37, ucslen(copyright_file_id) * 2);
int abstract_file_id_len = MIN(37, ucslen(abstract_file_id) * 2);
int biblio_file_id_len = MIN(37, ucslen(biblio_file_id) * 2);
vol->vol_desc_type[0] = 2;
memcpy(vol->std_identifier, "CD001", 5);
vol->vol_desc_version[0] = 1;
if (vol_id)
memcpy(vol->volume_id, vol_id, vol_id_len);
memcpy(vol->esc_sequences, "%/E", 3);
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
iso_bb(vol->block_size, t->block_size, 2);
iso_bb(vol->path_table_size, t->path_table_size_joliet, 4);
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4);
iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4);
write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record);
memcpy(vol->vol_set_id, volset_id, volset_id_len);
memcpy(vol->publisher_id, pub_id, pub_id_len);
memcpy(vol->data_prep_id, data_id, data_id_len);
memcpy(vol->system_id, system_id, system_id_len);
memcpy(vol->application_id, "APPID", application_id_len);
memcpy(vol->copyright_file_id, copyright_file_id, copyright_file_id_len);
memcpy(vol->abstract_file_id, abstract_file_id, abstract_file_id_len);
memcpy(vol->bibliographic_file_id, biblio_file_id, biblio_file_id_len);
iso_datetime_17(vol->vol_creation_time, t->now);
iso_datetime_17(vol->vol_modification_time, t->now);
iso_datetime_17(vol->vol_effective_time, t->now);
vol->file_structure_version[0] = 1;
free(vol_id);
free(volset_id);
free(pub_id);
free(data_id);
free(system_id);
free(application_id);
free(copyright_file_id);
free(abstract_file_id);
free(biblio_file_id);
}
static void
write_one_dir(struct ecma119_write_target *t,
struct joliet_tree_node *dir,
uint8_t *buf)
{
size_t i;
int j;
size_t len;
uint8_t *orig_buf = buf;
uint8_t *prior_buf = buf;
assert(dir->type == JOLIET_DIR);
/* write the "." and ".." entries first */
write_one_dir_record(t, dir, 0, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
write_one_dir_record(t, dir, 1, buf);
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
for (i = 0; i < dir->info.dir.nchildren; i++) {
write_one_dir_record(t, dir->info.dir.children[i], -1, buf);
len = ((struct ecma119_dir_record*) buf)->len_dr[0];
if ((buf + len - prior_buf) >= 2048) {
for (j = len - 1; j >= 0; j--) {
prior_buf[2048 + j] = buf[j];
buf[j] = 0;
}
prior_buf += 2048;
buf = prior_buf + len;
}
else {
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
}
}
assert (buf - orig_buf == dir->info.dir.len);
}
static void
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
{
size_t i;
struct joliet_tree_node *dir;
assert (t->curblock == t->dirlist_joliet[0]->info.dir.block);
for (i = 0; i < t->dirlist_len_joliet; i++) {
dir = t->dirlist_joliet[i];
write_one_dir(t, dir, buf);
buf += round_up(dir->info.dir.len, t->block_size);
}
}
void
joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_sup_vol_desc,
2048,
buf);
}
void
joliet_wr_l_path_table(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_l_path_table,
t->path_table_size_joliet,
buf);
}
void
joliet_wr_m_path_table(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_m_path_table,
t->path_table_size_joliet,
buf);
}
void
joliet_wr_dir_records(struct ecma119_write_target *t,
uint8_t *buf)
{
ecma119_start_chunking(t,
write_dirs,
t->total_dir_size_joliet,
buf);
}

View File

@ -0,0 +1,89 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file joliet.h
*
* Declare the filesystems trees that are Joliet-compatible and the public
* functions for tying them into an ecma119 volume.
*/
#ifndef LIBISO_JOLIET_H
#define LIBISO_JOLIET_H
#include <stdint.h>
#include <stdlib.h>
struct ecma119_write_target;
struct iso_tree_node;
enum joliet_node_type {
JOLIET_FILE,
JOLIET_DIR
};
struct joliet_dir_info {
struct joliet_tree_node **children;
size_t nchildren;
size_t len;
size_t block;
};
struct joliet_tree_node
{
uint16_t *name; /**< In UCS-2BE. */
size_t dirent_len;
struct joliet_tree_node *parent;
struct ecma119_write_target *target;
enum joliet_node_type type;
union {
struct iso_file *file;
struct joliet_dir_info dir;
} info;
};
/**
* Create a new joliet_tree that corresponds to the tree represented by
* \p iso_root.
*/
struct joliet_tree_node*
joliet_tree_create(struct ecma119_write_target *target,
struct iso_tree_node *iso_root);
/**
* Calculate the size of each directory in the joliet heirarchy.
*/
void
joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
/**
* Calculate the position of each directory in the joliet heirarchy.
*/
void
joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
/**
* Calculate the size of the joliet path table and fill in the list of
* directories.
*/
void
joliet_prepare_path_tables(struct ecma119_write_target *t);
void
joliet_tree_free(struct joliet_tree_node *root);
void
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
void
joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf);
#endif /* LIBISO_JOLIET_H */

View File

@ -0,0 +1,544 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Create an ISO-9660 data volume with Rock Ridge and Joliet extensions.
* Usage is easy:
* - Create a new volume.
* - Add files and directories.
* - Write the volume to a file or create a burn source for use with Libburn.
*/
#ifndef LIBISO_LIBISOFS_H
#define LIBISO_LIBISOFS_H
#include <sys/types.h>
/* #include <libburn.h> */
struct burn_source;
/**
* Data volume.
* @see volume.h for details.
*/
struct iso_volume;
/**
* A set of data volumes.
* @see volume.h for details.
*/
struct iso_volset;
/**
* A node in the filesystem tree.
* \see tree.h
*/
struct iso_tree_node;
/**
* El-Torito boot image
* \see eltorito.h
*/
struct el_torito_boot_image;
/**
* A directory in the filesystem tree.
* The first member of this is an iso_tree_node.
* \see tree.h
*/
struct iso_tree_node_dir;
/**
* Extensions addition to ECMA-119 (ISO-9660) image. Usage of at least
* one of these flags is highly recommended if the disc will be used on a
* modern OS.
*/
enum ecma119_extension_flag {
/**
* Add the standard Rock Ridge extensions. This adds POSIX filesystem
* features to the ECMA-119 image. Thus, usage of this flag is highly
* recommended for images used on GNU/Linux systems. With the usage
* of RR extension, the resulting image will have long filenames (up to
* 255 characters), deeper directory structure, POSIX permissions and
* owner info on files and directories, support for symbolic links or
* special files... All that attributes can be modified/setted with the
* appropiate function.
*/
ECMA119_ROCKRIDGE = (1<<0),
/**
* Add the non-standard Joliet extension to the image. This extension is
* heavily used in Microsoft Windows systems, so if you plan to use your
* disc on such a system you should add this extension. Usage of Joliet
* supplies longer filesystem length (up to 64 unicode characters), and
* deeper directory structure.
*/
ECMA119_JOLIET = (1<<1)
};
/**
* Flag used to hide a file in the RR/ISO or Joliet tree.
*
* \see iso_tree_node_set_hidden
*/
enum hide_node_flag {
LIBISO_HIDE_ON_RR = 1 << 0,
LIBISO_HIDE_ON_JOLIET = 1 << 1
};
/**
* El-Torito bootable image type.
*/
enum eltorito_boot_media_type {
ELTORITO_FLOPPY_EMUL,
ELTORITO_HARD_DISC_EMUL,
ELTORITO_NO_EMUL
};
enum ecma119_relaxed_constraints_flag {
ECMA119_OMIT_VERSION_NUMBERS = (1<<0),
/* 37 char filenames involves no version number */
ECMA119_37_CHAR_FILENAMES = (1<<1) | (1<<0),
ECMA119_NO_DIR_REALOCATION = (1<<2),
ECMA119_RELAXED_FILENAMES = (1<<3)
};
/**
* Holds the options for the image generation.
*/
struct ecma119_source_opts {
int volnum; /**< The volume in the set which you want to write (usually 0) */
int level; /**< ISO level to write at. */
int flags; /**< Which extensions to support. */
int relaxed_constraints; /**< see ecma119_relaxed_constraints_flag */
unsigned int no_cache_inodes:1;
/**< If use inode caching or not. Set it to 1 to prevent
* inode caching.
* Usage of inode caching allows detection of hard-links,
* which contents are only written once to disc this way.
* Don't use inode caching in systems with non unique inodes
* per device.
*/
unsigned int sort_files:1;
/**< If files should be sorted based on their weight. */
unsigned int default_mode:1;
/**<
* The default values for files and directory permissions,
* gid and uid. This option can be overwritten when set
* one of the following.
* 0 to use useful values, 1 to use node modes (this are
* the same as filesystem ones if not changed after added
* to tree).
*/
unsigned int replace_dir_mode:1;
/**<
* When 1, permissions for all dirs will be replaced by the
* specified in dir_mode field.
*/
unsigned int replace_file_mode:1;
/**<
* When 1, permissions for all files will be replaced by the
* specified in file_mode field.
*/
unsigned int replace_uid:1;
/**<
* When 1, uid of all nodes (both files and dirs) will be
* replaced by the specified in uid field.
*/
unsigned int replace_gid:1;
/**<
* When 1, gid of all nodes (both files and dirs) will be
* replaced by the specified in gid field.
*/
mode_t dir_mode; /**< Mode to use on dirs when replace_dir_mode is set. */
mode_t file_mode; /**< Mode to use on files when replace_file_mode is set. */
gid_t gid; /**< gid to use when replace_gid is set. */
uid_t uid; /**< uid to use when replace_uid is set. */
char *input_charset; /**< NULL to use default charset */
char *ouput_charset; /**< NULL to use default charset */
};
/**
* This will hold the error code for some functions, if them fail.
*/
int libisofs_errno;
/* an unexpected internal error */
#define INTERNAL_ERROR -1
/* file don't exists, or can't be stat'ed */
#define NO_FILE 1
/* user haven't read access to file */
#define NO_READ_ACCESS 2
/* unexpected file type, eg., passing a dir instead of a regular file */
#define UNEXPECTED_FILE_TYPE 3
/* invalid boot image size */
#define ELTORITO_WRONG_IMAGE_SIZE 4
/**
* Controls the bahavior of iso_tree_radd_dir function
*/
struct iso_tree_radd_dir_behavior {
char** excludes; /**< List of paths (file or directory) to be ignored. */
//int follow_sym_link;
int stop_on_error; /**< Stop when an error was found?. */
int error; /**< set to 1 on error */
//int notify_errors;
//char** errors;
};
/**
* Create a new volume.
* The parameters can be set to NULL if you wish to set them later.
*/
struct iso_volume *iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id);
struct iso_volume *iso_volume_new_with_root(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id,
struct iso_tree_node_dir *root);
/**
* Free a volume.
*/
void iso_volume_free(struct iso_volume *volume);
/**
* Free a set of data volumes.
*/
void iso_volset_free(struct iso_volset *volume);
/**
* Get the root directory for a volume.
*/
struct iso_tree_node_dir *iso_volume_get_root(const struct iso_volume *volume);
/**
* Fill in the volume identifier for a volume.
*/
void iso_volume_set_volume_id(struct iso_volume *volume,
const char *volume_id);
/**
* Fill in the publisher for a volume.
*/
void iso_volume_set_publisher_id(struct iso_volume *volume,
const char *publisher_id);
/**
* Fill in the data preparer for a volume.
*/
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
const char *data_preparer_id);
/**
* Fill in the system id for a volume. Up to 32 characters.
*/
void iso_volume_set_system_id(struct iso_volume *volume,
const char *system_id);
/**
* Fill in the application id for a volume. Up to 128 chars.
*/
void iso_volume_set_application_id(struct iso_volume *volume,
const char *application_id);
/**
* Fill copyright information for the volume. Usually this refers
* to a file on disc. Up to 37 characters.
*/
void iso_volume_set_copyright_file_id(struct iso_volume *volume,
const char *copyright_file_id);
/**
* Fill abstract information for the volume. Usually this refers
* to a file on disc. Up to 37 characters.
*/
void iso_volume_set_abstract_file_id(struct iso_volume *volume,
const char *abstract_file_id);
/**
* Fill biblio information for the volume. Usually this refers
* to a file on disc. Up to 37 characters.
*/
void iso_volume_set_biblio_file_id(struct iso_volume *volume,
const char *biblio_file_id);
/**
* Create a bootable volume by adding a El-Torito boot image.
*
* \param volume The volume to make bootable.
* \param image The tree node with the file to use as default boot image.
* \param type The boot media type. This can be one of 3 types:
* - Floppy emulation: Boot image files must be exactly
* 1200 kB, 1440 kB or 2880 kB.
* - Hard disc emulation: The image must begin with a master
* boot record with a single image.
* - No emulation. You should specify load segment and load size
* of image.
* \param dir The directory node where the boot catalog will be located
* in image. Usually both boot catalog and boot image will be
* located in the same dir, maybe /boot.
* \param name The name of the boot catalog.
*
* \return The default El-Torito bootable image. If specified image file
* seems to be not correct, this returns NULL and libisofs_errno
* is set propertly.
*
* \pre \p volume is a volume without any boot catalog yet
* \pre \p image is a file tree node already inserted in the volume tree.
* \pre \p dir is a directory node already inserted in the volume tree.
* \pre \p name There isn't any dir child with the same name.
*
*/
struct el_torito_boot_image *
iso_volume_create_boot_catalog(struct iso_volume *volume,
struct iso_tree_node *image,
enum eltorito_boot_media_type type,
struct iso_tree_node_dir *dir,
char *name);
/**
* Sets the load segment for the initial boot image. This is only for
* no emulation boot images, and is a NOP for other image types.
*/
void
el_torito_set_load_seg(struct el_torito_boot_image *bootimg, int segment);
/**
* Sets the number of sectors (512b) to be load at load segment during
* the initial boot procedure. This is only for
* no emulation boot images, and is a NOP for other image types.
*/
void
el_torito_set_load_size(struct el_torito_boot_image *bootimg, int sectors);
/**
* Marks the specified boot image as not bootable
*/
void
el_torito_set_no_bootable(struct el_torito_boot_image *bootimg);
/**
* Specifies that this image needs to be patched. This involves the writting
* of a 56 bytes boot information table at offset 8 of the boot image file.
* Be aware that libisofs will modify original boot image file, so do a backup
* if needed.
* This is needed for isolinux boot images.
*/
void
el_torito_set_write_boot_info(struct el_torito_boot_image *bootimg);
/**
* Locate a node by its path on disc.
*
* \param volume The volume to search in.
* \param path The path, in the image, of the file.
*
* \return The node found or NULL.
*
* TODO we need a way to allow developers know which kind of node is.
* Think about this when designing the read api
*/
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);
/**
* Add a file or a directory (recursively) to a volume by specifying its path on the volume.
*
* \param volume The volume to add the file to.
* \param disc_path The path on the disc at which to add the disc.
* \param path The path, on the local filesystem, of the file.
*
* \return The node for the file or NULL if the parent doesn't exists on the disc.
*/
//struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
// const char *disc_path,
// const char *path);
/**
* Creates a new, empty directory on the volume.
*
* \param volume The volume to add the directory to.
* \param disc_path The path on the volume at which to add the directory.
*
* \return A pointer to the newly created directory.
*/
//struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
// const char *disc_path);
/**
* Create a new Volume Set consisting of only one volume.
* @param volume The first and only volume for the volset to contain.
* @param volset_id The Volume Set ID.
* @return A new iso_volset.
*/
struct iso_volset *iso_volset_new(struct iso_volume *volume,
const char *volset_id);
/**
* Creates a new root dir for a filesystem tree
*/
struct iso_tree_node_dir *iso_tree_new_root();
/**
* Add a file to a directory.
*
* \param path The path, on the local filesystem, of the file.
*
* \pre \p parent is non-NULL.
* \pre \p path is non-NULL.
* \return An iso_tree_node_file whose path is \p path and whose parent is
* \p parent.
* On error, returns NULL and libisofs_errno is set appropriately:
* NO_FILE if path doesn't point to a valid file.
* NO_READ_ACCESS if user haven't read access on file
* UNEXPECTED_FILE_TYPE if path doesn't point to a regular file
*/
struct iso_tree_node *iso_tree_add_file(struct iso_tree_node_dir *parent,
const char *path);
/**
* Add a symbolic link to a directory.
*
* \param name The name of the symbolic link
* \param dest The distination of the link, i.e., the file this link points
* to
*
* \pre \p parent, name and dest are non-NULL.
*
* \return An iso_tree_node_symlink
*/
struct iso_tree_node *iso_tree_add_symlink(struct iso_tree_node_dir *parent,
const char *name, const char *dest);
/**
* Add a new, empty directory to the tree.
*
* \pre \p parent is non-NULL.
* \pre \p name is unique among the children and files belonging to \p parent.
* Also, it doesn't contain '/' characters.
*
* \post \p parent contains a child directory whose name is \p name and whose
* POSIX attributes are the same as \p parent's.
* \return a pointer to the newly created directory.
*/
struct iso_tree_node_dir *iso_tree_add_dir(struct iso_tree_node_dir *parent,
const char *name);
/* TODO iso_tree_new_special */
/**
* Add a file to a directory.
*
* \param path The path, on the local filesystem, of the file.
*
* \pre \p parent is non-NULL.
* \pre \p path is non-NULL and is a valid path to a file or directory on the local
* filesystem.
* \return An iso_tree_node whose path is \p path and whose parent is \p parent.
* On error, returns NULL and libisofs_errno is set appropriately:
* NO_FILE if path doesn't point to a valid file.
* NO_READ_ACCESS if user haven't read access on file
* UNEXPECTED_FILE_TYPE if path refers to non supported file type
* (at the momment, only dirs, symlinks and regular
* files are supported).
*/
struct iso_tree_node *iso_tree_add_node(struct iso_tree_node_dir *parent,
const char *path);
/**
* Recursively add an existing directory to the tree.
* Warning: when using this, you'll lose pointers to files or subdirectories.
* If you want to have pointers to all files and directories,
* use iso_tree_add_file, iso_tree_add_node and iso_tree_add_dir.
*
* \param path The path, on the local filesystem, of the directory to add.
*
* \pre \p parent is non-NULL.
* \pre \p path is non-NULL and is a valid path to a directory on the local
* filesystem.
*/
void iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior);
/**
* Set the name of a tree node (using the current locale).
*/
void iso_tree_node_set_name(struct iso_tree_node *node, const char *name);
/**
* Set if the node will be hidden in RR/ISO tree, Joliet tree or both.
*
* If the file is setted as hidden in one tree, it won't be included there, so
* it won't be visible in a OS accessing CD using that tree. For example,
* GNU/Linux systems access to Rock Ridge / ISO9960 tree in order to see
* what is recorded on CD, while MS Windows make use of the Joliet tree. If a
* file is hidden only in Joliet, it won't be visible in Windows systems,
* while still visible in Linux.
*
* If a file is hidden in both trees, it won't be written to image.
*
* \param node The node that is to be hidden.
* \param hide_attrs hide_node_flag's to set the trees in which file
* will be hidden.
*/
void iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs);
/**
* Set the group id for the node. This attribute is only useful when
* Rock Ridge extensions are enabled.
*/
void iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid);
/**
* Set the user id for the node. This attribute is only useful when
* Rock Ridge extensions are enabled.
*/
void iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid);
/**
* Set the permissions for the node. This attribute is only useful when
* Rock Ridge extensions are enabled.
*
* \param mode bitmask with the permissions of the node, as specified
* in 'man 2 stat'. The file type bitfields will be ignored,
* only file permissions will be modified.
*/
void iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode);
/**
* Sets the order in which a node will be written on image. High weihted files
* will be written first, so in a disc them will be written near the center.
*
* \param node The node which weight will be changed. If it's a dir, this
* function will change the weight of all its children. For nodes
* other that dirs or regular files, this function has no effect.
* \param w The weight as a integer number, the greater this value is, the
* closer from the begining of image the file will be written.
*/
void iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w);
/**
* Recursively print a directory to stdout.
* \param spaces The initial number of spaces on the left. Set to 0 if you
* supply a root directory.
*/
void iso_tree_print(const struct iso_tree_node *root, int spaces);
/** Create a burn_source which can be used as a data source for a track
*
* The volume set used to create the libburn_source can _not_ be modified
* until the libburn_source is freed.
*
* \param volumeset The volume set from which you want to write
* \param opts The options for image generation
*
* \pre \p volumeset is non-NULL
* \pre \p volnum is less than \p volset->volset_size.
* \return A burn_source to be used for the data source for a track
*/
struct burn_source* iso_source_new_ecma119(struct iso_volset *volumeset,
struct ecma119_source_opts *opts);
#endif /* LIBISO_LIBISOFS_H */

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 FIXME: this is rockridge */
void
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
unsigned char *ER = malloc(182);
assert(dir->type == ECMA119_DIR);
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
susp_append(t, &dir->info.dir.self_susp, ER);
}
/* create a PX field from the permissions on the current node. */
uint8_t *rrip_make_PX(struct ecma119_write_target *t,
struct ecma119_tree_node *node)
{
uint8_t *PX = malloc(44);
PX[0] = 'P';
PX[1] = 'X';
PX[2] = 44;
PX[3] = 1;
iso_bb(&PX[4], node->attrib.st_mode, 4);
iso_bb(&PX[12], node->attrib.st_nlink, 4);
iso_bb(&PX[20], node->attrib.st_uid, 4);
iso_bb(&PX[28], node->attrib.st_gid, 4);
iso_bb(&PX[36], node->attrib.st_ino, 4);
return PX;
}
/** See IEEE 1282 4.1.1 */
void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
susp_append(t, &node->susp, rrip_make_PX(t, node));
if (node->type == ECMA119_DIR) {
susp_append(t, &node->info.dir.self_susp, rrip_make_PX(t, node));
susp_append(t, &node->info.dir.parent_susp, rrip_make_PX(t, node));
}
}
void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *PN = malloc(20);
PN[0] = 'P';
PN[1] = 'N';
PN[2] = 20;
PN[3] = 1;
iso_bb(&PN[4], node->attrib.st_dev >> 32, 4);
iso_bb(&PN[12], node->attrib.st_dev & 0xffffffff, 4);
susp_append(t, &node->susp, PN);
}
static void rrip_SL_append_comp(int *n, uint8_t ***comps,
char *s, int size, char fl)
{
uint8_t *comp = malloc(size + 2);
(*n)++;
comp[0] = fl;
comp[1] = size;
*comps = realloc(*comps, (*n) * sizeof(void*));
(*comps)[(*n) - 1] = comp;
if (size) {
memcpy(&comp[2], s, size);
}
}
static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
uint8_t ***comps)
{
int size = cur - prev;
if (size == 0) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
return;
}
if (size == 1 && prev[0] == '.') {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
return;
}
if (size == 2 && !strncmp(prev, "..", 2)) {
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
return;
}
/* we can't make a component any bigger than 250 (is this really a
problem)? because then it won't fit inside the SL field */
while (size > 248) {
size -= 248;
rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
}
rrip_SL_append_comp(n_comp, comps, prev, size, 0);
}
void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
int path_size;
char *path = NULL, *cur, *prev;
int i, j;
uint8_t **comp = NULL;
int n_comp = 0;
int total_comp_len = 0;
int written = 0, pos;
uint8_t *SL;
path = node->info.dest;
path_size = strlen(path);
prev = path;
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
rrip_SL_add_component(prev, cur, &n_comp, &comp);
cur++;
prev = cur;
}
/* if there was no trailing '/', we need to add the last component. */
if (prev == path || prev != &path[path_size - 1]) {
rrip_SL_add_component(prev, &path[path_size], &n_comp, &comp);
}
for (i = 0; i < n_comp; i++) {
total_comp_len += comp[i][1] + 2;
if (total_comp_len > 250) {
total_comp_len -= comp[i][1] + 2;
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 1; /* CONTINUE */
pos = 5;
for (j = written; j < i; j++) {
memcpy(&SL[pos], comp[j], comp[j][2]);
pos += comp[j][2];
}
susp_append(t, &node->susp, SL);
written = i - 1;
total_comp_len = comp[i][1];
}
}
SL = malloc(total_comp_len + 5);
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 0;
pos = 5;
for (j = written; j < n_comp; j++) {
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
pos += comp[j][1] + 2;
}
susp_append(t, &node->susp, SL);
/* free the components */
for (i = 0; i < n_comp; i++) {
free(comp[i]);
}
free(comp);
}
static void rrip_add_NM_single(struct ecma119_write_target *t,
struct susp_info *susp,
char *name, int size, int flags)
{
uint8_t *NM = malloc(size + 5);
NM[0] = 'N';
NM[1] = 'M';
NM[2] = size + 5;
NM[3] = 1;
NM[4] = flags;
if (size) {
memcpy(&NM[5], name, size);
}
susp_append(t, susp, NM);
}
void
rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
char *name = node->full_name;
int len = name ? strlen(name) : 0;
char *pos = name;
if (!len)
return;
if (node->type == ECMA119_DIR) {
rrip_add_NM_single(t, &node->info.dir.self_susp, pos, 0, 1 << 1);
rrip_add_NM_single(t, &node->info.dir.parent_susp, pos, 0, 1 << 2);
}
while (len > 250) {
rrip_add_NM_single(t, &node->susp, pos, 250, 1);
len -= 250;
pos += 250;
}
rrip_add_NM_single(t, &node->susp, pos, len, 0);
}
void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *CL = calloc(1, 12);
CL[0] = 'C';
CL[1] = 'L';
CL[2] = 12;
CL[3] = 1;
susp_append(t, &node->susp, CL);
}
void
rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *PL = calloc(1, 12);
PL[0] = 'P';
PL[1] = 'L';
PL[2] = 12;
PL[3] = 1;
susp_append(t, &node->info.dir.parent_susp, PL);
}
void
rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *RE = malloc(4);
RE[0] = 'R';
RE[1] = 'E';
RE[2] = 4;
RE[3] = 1;
susp_append(t, &node->susp, RE);
}
void
rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
{
uint8_t *TF = malloc(5 + 3 * 7);
TF[0] = 'T';
TF[1] = 'F';
TF[2] = 5 + 3 * 7;
TF[3] = 1;
TF[4] = (1 << 1) | (1 << 2) | (1 << 3);
iso_datetime_7(&TF[5], node->attrib.st_mtime);
iso_datetime_7(&TF[12], node->attrib.st_atime);
iso_datetime_7(&TF[19], node->attrib.st_ctime);
susp_append(t, &node->susp, TF);
}
void
rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
int i;
assert(dir->type == ECMA119_DIR);
if (dir->parent != dir->info.dir.real_parent) {
uint8_t *PL = susp_find(&dir->info.dir.parent_susp, "PL");
assert(PL);
iso_bb(&PL[4], dir->info.dir.real_parent->info.dir.block, 4);
}
for (i = 0; i < dir->info.dir.nchildren; i++) {
struct ecma119_tree_node *ch = dir->info.dir.children[i];
if (ch->type == ECMA119_PLACEHOLDER) {
uint8_t *CL = susp_find(&ch->susp, "CL");
assert(CL);
iso_bb(&CL[4], ch->info.real_me->info.dir.block, 4);
} else if (ch->type == ECMA119_DIR) {
rrip_finalize(t, ch);
}
}
}

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,256 @@
/* 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);
if (dir->info.dir.depth != 1) {
susp_fin_CE(t, dir);
}
for (i = 0; i < dir->info.dir.nchildren; i++) {
if (dir->info.dir.children[i]->type == ECMA119_DIR)
susp_finalize(t, dir->info.dir.children[i]);
}
}
void susp_write(struct ecma119_write_target *t,
struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = 0; i < susp->n_fields_fit; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
unsigned char *buf)
{
int i;
int pos = 0;
for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
memcpy(&buf[pos], susp->susp_fields[i],
susp->susp_fields[i][2]);
pos += susp->susp_fields[i][2];
}
}
void susp_free_fields(struct susp_info *susp)
{
int i;
for (i=0; 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,401 @@
/* 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);
parent->nchildren++;
parent->children =
realloc(parent->children, parent->nchildren * sizeof(void*));
parent->children[parent->nchildren-1] = child;
child->parent = parent;
}
struct iso_tree_node_dir*
iso_tree_new_root()
{
struct iso_tree_node_dir *root;
root = calloc(1, sizeof(struct iso_tree_node_dir));
set_default_stat(&root->node.attrib);
root->node.attrib.st_mode = S_IFDIR | 0777;
root->node.type = LIBISO_NODE_DIR;
return root;
}
struct iso_tree_node*
iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path)
{
struct iso_tree_node_file *f;
char *p;
struct stat st;
assert( parent && path);
if (lstat(path, &st) == -1) {
libisofs_errno = NO_FILE;
return NULL;
}
if ( !S_ISREG(st.st_mode) ) {
libisofs_errno = UNEXPECTED_FILE_TYPE;
return NULL;
}
if ( access(path, R_OK) ) {
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
f = calloc(1, sizeof(struct iso_tree_node_file));
/* fill fields */
f->node.attrib = st;
f->path = strdup(path);
f->node.type = LIBISO_NODE_FILE;
p = strdup(path); /* because basename() might modify its arg */
f->node.name = strdup( basename(p) );
free(p);
/* add to parent (this also sets f->node->parent) */
iso_tree_add_child(parent, (struct iso_tree_node*) f);
return (struct iso_tree_node*) f;
}
struct iso_tree_node*
iso_tree_add_symlink(struct iso_tree_node_dir *parent,
const char *name, const char *dest)
{
struct iso_tree_node_symlink *link;
assert( parent && name && dest);
link = calloc(1, sizeof(struct iso_tree_node_symlink));
/* fill fields */
set_default_stat(&link->node.attrib);
link->node.attrib.st_mode |= S_IFLNK;//TODO Not needed
link->node.name = strdup(name);
link->node.type = LIBISO_NODE_SYMLINK;
link->dest = strdup(dest);
/* add to parent (this also sets link->node->parent) */
iso_tree_add_child(parent, (struct iso_tree_node*) link);
return (struct iso_tree_node*) link;
}
struct iso_tree_node_dir*
iso_tree_add_dir(struct iso_tree_node_dir *parent,
const char *name)
{
struct iso_tree_node_dir *dir;
assert( parent && name );
dir = calloc(1, sizeof(struct iso_tree_node_dir));
dir->node.attrib = parent->node.attrib;
dir->node.type = LIBISO_NODE_DIR;
dir->node.name = strdup(name);
iso_tree_add_child(parent, (struct iso_tree_node*) dir);
return dir;
}
void
iso_tree_node_set_name(struct iso_tree_node *node, const char *name)
{
free(node->name);
node->name = strdup(name);
}
void
iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs)
{
assert(node);
node->hide_flags = hide_attrs;
}
void
iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid)
{
assert(node);
node->attrib.st_gid = gid;
}
void
iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid)
{
assert(node);
node->attrib.st_uid = uid;
}
void
iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode)
{
assert(node);
node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) |
(mode & ~S_IFMT);
}
void
iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w)
{
assert(node);
if ( ISO_ISDIR(node) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) node;
for (i=0; i < dir->nchildren; i++) {
iso_tree_node_set_sort_weight(dir->children[i], w);
}
} else if ( ISO_ISREG(node) ) {
struct iso_tree_node_file *file;
file = (struct iso_tree_node_file *) node;
file->sort_weight = w;
}
}
struct iso_tree_node*
iso_tree_add_node(struct iso_tree_node_dir *parent,
const char *path)
{
struct stat st;
struct iso_tree_node *node;
assert( parent && path);
if (lstat(path, &st) == -1) {
libisofs_errno = NO_FILE;
return NULL;
}
if ( access(path, R_OK) ) {
libisofs_errno = NO_READ_ACCESS;
return NULL;
}
switch (st.st_mode & S_IFMT) {
case S_IFREG:
/* regular file */
node = iso_tree_add_file(parent, path);
break;
case S_IFLNK:
/* symlink */
{
char dest[PATH_MAX];
char *p;
int n;
n = readlink(path, dest, PATH_MAX);
if ( n == -1 ) {
libisofs_errno = INTERNAL_ERROR;
return NULL;
}
dest[n] = '\0';
p = strdup(path); /* because basename() might modify its arg */
node = iso_tree_add_symlink(parent, basename(p), dest);
free(p);
node->attrib = st;
}
break;
case S_IFDIR:
/* directory */
{
char *p;
p = strdup(path); /* because basename() might modify its arg */
node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p));
free(p);
node->attrib = st;
}
break;
default:
libisofs_errno = UNEXPECTED_FILE_TYPE;
node = NULL;
break;
}
return node;
}
void
iso_tree_free(struct iso_tree_node *root)
{
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) root;
for (i=0; i < dir->nchildren; i++) {
iso_tree_free(dir->children[i]);
}
free(dir->children);
} else if ( ISO_ISLNK(root) ) {
struct iso_tree_node_symlink *link;
link = (struct iso_tree_node_symlink *) root;
free(link->dest);
} else if ( ISO_ISREG(root) ) {
struct iso_tree_node_file *file;
file = (struct iso_tree_node_file *) root;
free(file->path);
}
free(root->name);
free(root);
}
static void
iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior,
struct iso_hash_table *excludes)
{
struct iso_tree_node *new;
DIR *dir;
struct dirent *ent;
dir = opendir(path);
if (!dir) {
warn("couldn't open directory %s: %s\n", path, strerror(errno));
return;
}
while ((ent = readdir(dir))) {
char child[strlen(ent->d_name) + strlen(path) + 2];
if (behavior->stop_on_error & behavior->error)
break;
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
//TODO check if path already finished in '/'
sprintf(child, "%s/%s", path, ent->d_name);
/* see if this child is excluded. */
if (iso_exclude_lookup(excludes, child))
continue;
new = iso_tree_add_node(parent, child);
if (!new || !ISO_ISDIR(new)) {
if (!new)
behavior->error = 1;
continue;
}
iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child,
behavior, excludes);
}
closedir(dir);
return;
}
void
iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path,
struct iso_tree_radd_dir_behavior *behavior)
{
struct iso_tree_node_dir *dir;
struct iso_hash_table table = { {0,}, 0};
assert ( parent && path );
behavior->error = 0;
/* initialize exclude hash_table */
if ( behavior->excludes ) {
char *exclude;
int i = 0;
while ( (exclude = behavior->excludes[i++]) ) {
iso_exclude_add_path(&table, exclude);
}
}
/* recurse into dir */
iso_tree_radd_dir_aux(parent, path, behavior, &table);
/* clear hashtable */
iso_exclude_empty(&table);
return dir;
}
void
iso_tree_print(const struct iso_tree_node *root, int spaces)
{
char sp[spaces+1];
memset(sp, ' ', spaces);
sp[spaces] = '\0';
printf("%s%s\n", sp, root->name);
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir;
dir = (struct iso_tree_node_dir *) root;
for (i=0; i < dir->nchildren; i++) {
iso_tree_print(dir->children[i], spaces+2);
}
}
}
void
iso_tree_print_verbose(const struct iso_tree_node *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces)
{
(ISO_ISDIR(root) ? dir : file)
(root, callback_data, spaces);
if ( ISO_ISDIR(root) ) {
size_t i;
struct iso_tree_node_dir *dir_node;
dir_node = (struct iso_tree_node_dir *) root;
for (i=0; i < dir_node->nchildren; i++) {
iso_tree_print_verbose(dir_node->children[i], dir,
file, callback_data, spaces+2);
}
}
}

View File

@ -0,0 +1,177 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* \file tree.h
*
* Declare the structure of a libisofs filesystem tree. The files in this
* tree can come from either the local filesystem or from another .iso image
* (for multisession).
*
* This tree preserves as much information as it can about the files; names
* are stored in wchar_t and we preserve POSIX attributes. This tree does
* *not* include information that is necessary for writing out, for example,
* an ISO level 1 tree. That information will go in a different tree because
* the structure is sufficiently different.
*/
#ifndef LIBISO_TREE_H
#define LIBISO_TREE_H
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#include <wchar.h>
#include "libisofs.h"
//enum file_location {
// LIBISO_FILESYS,
// LIBISO_PREVSESSION,
// LIBISO_NONE /**< for files/dirs that were added with
// * iso_tree_add_new_XXX. */
//};
/**
* This tells us where to read the data from a file. Either we read from the
* local filesystem or we just point to the block on a previous session.
*/
//struct iso_file_location
//{
// enum file_location type;
// /* union {*/
// char *path; /* in the current locale */
// uint32_t block;
// /* };*/
//};
enum iso_tree_node_type {
LIBISO_NODE_DIR,
LIBISO_NODE_FILE,
LIBISO_NODE_SYMLINK,
LIBISO_NODE_BOOTCATALOG
};
/**
* A node in the filesystem tree.
*/
struct iso_tree_node
{
struct iso_tree_node_dir *parent;
char *name;
struct stat attrib; /**< The POSIX attributes of this node as
* documented in "man 2 stat". */
int hide_flags; /**< If the node is to be hidden in RR/ISO or
* Joilet tree */
enum iso_tree_node_type type;
};
/**
* A node in the filesystem tree that represents a regular file
*/
struct iso_tree_node_file
{
struct iso_tree_node node;
char *path; /**< the path of the file on local filesystem */
int sort_weight; /**< It sorts the order in which the file data is
* written to the CD image. Higher weighting files
* are written at the beginning of image */
/* when read from an existing ISO image, we need to store the
* block where file contents are written, and not the path.
* Maybe instead of a char *path we will need to go back to
* struct iso_file_location loc;
*/
/* struct iso_file_location loc; */
/**< Only used for regular files and symbolic
* links (ie. files for which we might have to
* copy data). */
};
/**
* A node in the filesystem tree that represents a symbolic link
*/
struct iso_tree_node_symlink
{
struct iso_tree_node node;
char *dest; /**< Destination of the link */
};
/**
* A directory on the filesystem tree
*/
struct iso_tree_node_dir
{
struct iso_tree_node node;
size_t nchildren; /**< The number of children of this
* directory (if this is a directory). */
struct iso_tree_node **children;
};
/**
* Recursively free a directory.
*
* \param root The root of the directory heirarchy to free.
*
* \pre \p root is non-NULL.
*/
void iso_tree_free(struct iso_tree_node *root);
/**
* Adds a child to a directory
*/
void iso_tree_add_child(struct iso_tree_node_dir *parent,
struct iso_tree_node *child);
/**
* A function that prints verbose information about a directory.
*
* \param dir The directory about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_dir_callback) (const struct iso_tree_node *dir,
void *data,
int spaces);
/**
* A function that prints verbose information about a file.
*
* \param dir The file about which to print information.
* \param data Unspecified function-dependent data.
* \param spaces The number of spaces to prepend to the output.
*
* \see iso_tree_print_verbose
*/
typedef void (*print_file_callback) (const struct iso_tree_node *file,
void *data,
int spaces);
/**
* Recursively print a directory heirarchy. For each node in the directory
* heirarchy, call a callback function to print information more verbosely.
*
* \param root The root of the directory heirarchy to print.
* \param dir The callback function to call for each directory in the tree.
* \param file The callback function to call for each file in the tree.
* \param callback_data The data to pass to the callback functions.
* \param spaces The number of spaces to prepend to the output.
*
* \pre \p root is not NULL.
* \pre Neither of the callback functions modifies the directory heirarchy.
*/
void iso_tree_print_verbose(const struct iso_tree_node *root,
print_dir_callback dir,
print_file_callback file,
void *callback_data,
int spaces);
#define ISO_ISDIR(n) (n->type == LIBISO_NODE_DIR)
#define ISO_ISREG(n) (n->type == LIBISO_NODE_FILE)
#define ISO_ISLNK(n) (n->type == LIBISO_NODE_SYMLINK)
#endif /* LIBISO_TREE_H */

View File

@ -0,0 +1,842 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set noet ts=8 sts=8 sw=8 : */
/**
* Utility functions for the Libisofs library.
*/
#ifdef HAVE_CONFIG_H
#include <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 <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 size = flag & (1<<1) ? 37 : 33;
if (!src)
return NULL;
dest = malloc(size+1);
if (flag & ECMA119_RELAXED_FILENAMES) {
strncpy(dest, src, size);
dest[size] = '\0'; /* ensure 37 / 33 max length */
pos = strlen(dest);
pos = pos < (size == 37 ? 37 : 31) ? pos : (size == 37 ? 37 : 31);
if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) {
dest[pos++] = ';';
dest[pos++] = '1';
}
dest[pos] = '\0';
return dest;
}
/* no relaxed filenames */
dot = strrchr(src, '.');
size_t max = size == 37 ? 36 : 30;
/* Since the maximum length can be divided freely over the name and
extension, we need to calculate their new lengths (lnname and
lnext). If the original filename is too long, we start by trimming
the extension, but keep a minimum extension length of 3. */
if (dot == NULL || *(dot + 1) == '\0') {
lname = strlen(src);
lnname = (lname > max) ? max : lname;
lext = lnext = 0;
} else {
lext = strlen(dot + 1);
lname = strlen(src) - lext - 1;
lnext = (strlen(src) > max + 1 && lext > 3)
? (lname < max - 3 ? max - lname : 3) : lext;
lnname = (strlen(src) > max +1) ? max - lnext : lname;
}
if (lnname == 0 && lnext == 0) {
free(src);
free(dest);
return NULL;
}
pos = 0;
/* Convert up to lnname characters of the filename. */
for (i = 0; i < lnname; i++) {
char c = toupper(src[i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
dest[pos++] = '.';
/* Convert up to lnext characters of the extension, if any. */
for (i = 0; i < lnext; i++) {
char c = toupper(src[lname + 1 + i]);
dest[pos++] = valid_d_char(c) ? c : '_';
}
if ( !(flag & ECMA119_OMIT_VERSION_NUMBERS) ) {
dest[pos++] = ';';
dest[pos++] = '1';
}
dest[pos] = '\0';
dest = (char *)realloc(dest, pos + 1);
free(src);
return dest;
}
char *
iso_p_fileid(const char *src, const char *icharset)
{
char *ret = str2ascii(src, icharset);
size_t i, len;
if (!ret)
return NULL;
len = strlen(ret);
for (i = 0; i < len; i++) {
if (!valid_p_char(ret[i])) {
ret[i] = (uint16_t)'_';
}
}
return ret;
}
uint16_t *
iso_j_id(const char *src_arg, const char *icharset)
{
uint16_t *j_str = str2ucs(src_arg, icharset);
size_t len = ucslen(j_str);
size_t n;
if (len > 128) {
j_str[128] = '\0';
len = 128;
}
for (n = 0; n < len; n++)
if (!valid_j_char(j_str[n]))
j_str[n] = '_';
return j_str;
}
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[i] = (num >> (8 * i)) & 0xff;
}
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
{
int i;
assert(bytes <= 4);
for (i = 0; i < bytes; ++i)
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
}
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
{
iso_lsb(buf, num, bytes);
iso_msb(buf+bytes, num, bytes);
}
void iso_datetime_7(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
int tzoffset;
struct tm tm;
if (!tzsetup) {
tzset();
tzsetup = 1;
}
localtime_r(&t, &tm);
buf[0] = tm.tm_year;
buf[1] = tm.tm_mon + 1;
buf[2] = tm.tm_mday;
buf[3] = tm.tm_hour;
buf[4] = tm.tm_min;
buf[5] = tm.tm_sec;
#ifdef HAVE_TM_GMTOFF
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
tzoffset = timezone / 60 / 15;
#endif
if (tzoffset > 52)
tzoffset -= 101;
buf[6] = tzoffset;
}
time_t iso_datetime_read_7(const uint8_t *buf)
{
struct tm tm;
tm.tm_year = buf[0];
tm.tm_mon = buf[1] + 1;
tm.tm_mday = buf[2];
tm.tm_hour = buf[3];
tm.tm_min = buf[4];
tm.tm_sec = buf[5];
return mktime(&tm) - buf[6] * 60 * 60;
}
void iso_datetime_17(unsigned char *buf, time_t t)
{
static int tzsetup = 0;
static int tzoffset;
struct tm tm;
if (t == (time_t) - 1) {
/* unspecified time */
memset(buf, '0', 16);
buf[16] = 0;
} else {
if (!tzsetup) {
tzset();
tzsetup = 1;
}
localtime_r(&t, &tm);
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
sprintf((char*)&buf[6], "%02d", tm.tm_mday);
sprintf((char*)&buf[8], "%02d", tm.tm_hour);
sprintf((char*)&buf[10], "%02d", tm.tm_min);
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
memcpy(&buf[14], "00", 2);
#ifdef HAVE_TM_GMTOFF
tzoffset = tm.tm_gmtoff / 60 / 15;
#else
tzoffset = timezone / 60 / 15;
#endif
if (tzoffset > 52)
tzoffset -= 101;
buf[16] = tzoffset;
}
}
time_t iso_datetime_read_17(const uint8_t *buf)
{
struct tm tm;
sscanf((char*)&buf[0], "%4d", &tm.tm_year);
sscanf((char*)&buf[4], "%2d", &tm.tm_mon);
sscanf((char*)&buf[6], "%2d", &tm.tm_mday);
sscanf((char*)&buf[8], "%2d", &tm.tm_hour);
sscanf((char*)&buf[10], "%2d", &tm.tm_min);
sscanf((char*)&buf[12], "%2d", &tm.tm_sec);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
return mktime(&tm) - buf[16] * 60 * 60;
}
size_t ucslen(const uint16_t *str)
{
int i;
for (i=0; str[i]; i++)
;
return i;
}
/**
* Although each character is 2 bytes, we actually compare byte-by-byte
* (thats what the spec says).
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2)
{
const char *s = (const char*)s1;
const char *t = (const char*)s2;
size_t len1 = ucslen(s1);
size_t len2 = ucslen(s2);
size_t i, len = MIN(len1, len2) * 2;
for (i=0; i < len; i++) {
if (s[i] < t[i]) {
return -1;
} else if (s[i] > t[i]) {
return 1;
}
}
if (len1 < len2)
return -1;
else if (len1 > len2)
return 1;
return 0;
}
uint32_t iso_read_lsb(const uint8_t *buf, int bytes)
{
int i;
uint32_t ret = 0;
for (i=0; i<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)
{
uint32_t v1 = iso_read_lsb(buf, bytes);
uint32_t v2 = iso_read_msb(buf+bytes, bytes);
assert(v1 == v2);
return v1;
}

View File

@ -0,0 +1,136 @@
/* -*- 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);
uint32_t iso_read_bb(const uint8_t *buf, int bytes);
/** Records the date/time into a 7 byte buffer (9.1.5) */
void iso_datetime_7(uint8_t *buf, time_t t);
/** Records the date/time into a 17 byte buffer (8.4.26.1) */
void iso_datetime_17(uint8_t *buf, time_t t);
time_t iso_datetime_read_7(const uint8_t *buf);
time_t iso_datetime_read_17(const uint8_t *buf);
/**
* Like strlen, but for Joliet strings.
*/
size_t ucslen(const uint16_t *str);
/**
* Like strcmp, but for Joliet strings.
*/
int ucscmp(const uint16_t *s1, const uint16_t *s2);
#endif /* LIBISO_UTIL_H */

View File

@ -0,0 +1,258 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#include <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_free(struct iso_volset *volset)
{
if (--volset->refcount < 1) {
int i;
for (i = 0; i < volset->volset_size; i++) {
iso_volume_free(volset->volume[i]);
}
free(volset->volume);
free(volset->volset_id);
free(volset);
}
}
struct iso_volume*
iso_volume_new(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id)
{
return iso_volume_new_with_root(volume_id,
publisher_id,
data_preparer_id,
NULL);
}
struct iso_volume*
iso_volume_new_with_root(const char *volume_id,
const char *publisher_id,
const char *data_preparer_id,
struct iso_tree_node_dir *root)
{
struct iso_volume *volume;
volume = calloc(1, sizeof(struct iso_volume));
volume->refcount = 1;
volume->root = root ? root : iso_tree_new_root();
if (volume_id != NULL)
volume->volume_id = strdup(volume_id);
if (publisher_id != NULL)
volume->publisher_id = strdup(publisher_id);
if (data_preparer_id != NULL)
volume->data_preparer_id = strdup(data_preparer_id);
return volume;
}
void
iso_volume_free(struct iso_volume *volume)
{
/* Only free if no references are in use. */
if (--volume->refcount < 1) {
iso_tree_free( (struct iso_tree_node*) volume->root);
free(volume->volume_id);
free(volume->publisher_id);
free(volume->data_preparer_id);
free(volume->system_id);
free(volume->application_id);
free(volume->copyright_file_id);
free(volume->abstract_file_id);
free(volume->biblio_file_id);
if (volume->bootcat)
el_torito_boot_catalog_free(volume->bootcat);
free(volume);
}
}
struct iso_tree_node_dir *
iso_volume_get_root(const struct iso_volume *volume)
{
return volume->root;
}
void iso_volume_set_volume_id(struct iso_volume *volume,
const char *volume_id)
{
free(volume->volume_id);
volume->volume_id = strdup(volume_id);
}
void iso_volume_set_publisher_id(struct iso_volume *volume,
const char *publisher_id)
{
free(volume->publisher_id);
volume->publisher_id = strdup(publisher_id);
}
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
const char *data_preparer_id)
{
free(volume->data_preparer_id);
volume->data_preparer_id = strdup(data_preparer_id);
}
void iso_volume_set_system_id(struct iso_volume *volume,
const char *system_id)
{
free(volume->system_id);
volume->system_id = strdup(system_id);
}
void iso_volume_set_application_id(struct iso_volume *volume,
const char *application_id)
{
free(volume->application_id);
volume->application_id = strdup(application_id);
}
void iso_volume_set_copyright_file_id(struct iso_volume *volume,
const char *copyright_file_id)
{
free(volume->copyright_file_id);
volume->copyright_file_id = strdup(copyright_file_id);
}
void iso_volume_set_abstract_file_id(struct iso_volume *volume,
const char *abstract_file_id)
{
free(volume->abstract_file_id);
volume->abstract_file_id = strdup(abstract_file_id);
}
void iso_volume_set_biblio_file_id(struct iso_volume *volume,
const char *biblio_file_id)
{
free(volume->biblio_file_id);
volume->biblio_file_id = strdup(biblio_file_id);
}
struct iso_tree_node *
iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
{
struct iso_tree_node *node;
struct iso_tree_node_dir *dir;
char *ptr, *brk_info, *component;
/* get the first child at the root of the volume
* that is "/" */
dir = iso_volume_get_root(volume);
node = (struct iso_tree_node *)dir;
if (!strcmp(path, "/"))
return node;
if (!dir->nchildren)
return NULL;
ptr = strdup(path);
/* get the first component of the path */
component=strtok_r(ptr, "/", &brk_info);
while (component) {
size_t max;
size_t i;
if ( !ISO_ISDIR(node) ) {
node=NULL;
break;
}
dir = (struct iso_tree_node_dir *)node;
/* search among all the children of this directory if this path component exists */
max=dir->nchildren;
for (i=0; i < max; i++) {
if (!strcmp(component, dir->children[i]->name)) {
node=dir->children[i];
break;
}
}
/* see if a node could be found */
if (i==max) {
node=NULL;
break;
}
component=strtok_r(NULL, "/", &brk_info);
}
free(ptr);
return node;
}
//
//struct iso_tree_node *
//iso_tree_volume_add_path(struct iso_volume *volume,
// const char *disc_path,
// const char *path)
//{
// char *tmp;
// struct iso_tree_node *node;
// struct iso_tree_node *parent_node;
//
// tmp=strdup(disc_path);
// parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
// free(tmp);
//
// if (!parent_node)
// return NULL;
//
// node = iso_tree_radd_dir(parent_node, path);
// if (!node)
// return NULL;
//
// tmp=strdup(disc_path);
// iso_tree_node_set_name(node, basename(tmp));
// free(tmp);
//
// return node;
//}
//
//struct iso_tree_node *
//iso_tree_volume_add_new_dir(struct iso_volume *volume,
// const char *disc_path)
//{
// char *tmp;
// struct iso_tree_node *node;
// struct iso_tree_node *parent_node;
//
// tmp=strdup(disc_path);
// parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
// free(tmp);
//
// if (!parent_node)
// return NULL;
//
// tmp=strdup(disc_path);
// node = iso_tree_add_new_dir(parent_node, basename(tmp));
// free(tmp);
//
// return node;
//}

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,141 @@
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* vim: set ts=8 sts=8 sw=8 noet : */
#define _GNU_SOURCE
#include "libisofs.h"
#include "libburn/libburn.h"
#include <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:h";
extern char *optarg;
extern int optind;
void usage()
{
printf("test [OPTIONS] DIRECTORY OUTPUT\n");
}
void help()
{
printf(
"Options:\n"
" -J Add Joliet support\n"
" -R Add Rock Ridge support\n"
" -L <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 iso_volset *volset;
struct iso_volume *volume;
struct iso_tree_node_dir *root;
struct burn_source *src;
unsigned char buf[2048];
FILE *fd;
int c;
struct iso_tree_radd_dir_behavior behav = {0,0,0};
int level=1, flags=0;
char *boot_img = NULL;
while ((c = getopt(argc, argv, optstring)) != -1) {
switch(c) {
case 'h':
usage();
help();
exit(0);
break;
case 'J':
flags |= ECMA119_JOLIET;
break;
case 'R':
flags |= ECMA119_ROCKRIDGE;
break;
case 'L':
level = atoi(optarg);
break;
case 'b':
boot_img = optarg;
break;
case '?':
usage();
exit(1);
break;
}
}
if (argc < 2) {
printf ("Please pass directory from which to build ISO\n");
usage();
return 1;
}
if (argc < 3) {
printf ("Please supply output file\n");
usage();
return 1;
}
fd = fopen(argv[optind+1], "w");
if (!fd) {
err(1, "error opening output file");
}
root = iso_tree_new_root();
iso_tree_radd_dir(root, argv[optind], &behav);
if (!root) {
err(1, "error opening input directory");
}
volume = iso_volume_new_with_root( "VOLID", "PUBID", "PREPID", root );
if ( boot_img ) {
/* adds El-Torito boot info. Tunned for isolinux */
struct iso_tree_node_dir *boot = (struct iso_tree_node_dir *)
iso_tree_volume_path_to_node(volume, "isolinux");
struct iso_tree_node *img = iso_tree_volume_path_to_node(volume, boot_img);
if (!img) {
err(1, "boot image patch is not valid");
}
struct el_torito_boot_image *bootimg =
iso_volume_create_boot_catalog(volume, img, ELTORITO_NO_EMUL,
boot, "boot.cat");
el_torito_set_load_size(bootimg, 4);
el_torito_set_write_boot_info(bootimg);
}
volset = iso_volset_new( volume, "VOLSETID" );
/* some tests */
iso_volume_set_application_id(volume, "Libburnia");
iso_volume_set_copyright_file_id(volume, "LICENSE");
int constraints = ECMA119_OMIT_VERSION_NUMBERS |
ECMA119_37_CHAR_FILENAMES | ECMA119_NO_DIR_REALOCATION |
ECMA119_RELAXED_FILENAMES;
struct ecma119_source_opts opts = {0, level, flags, constraints, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, "UTF-8", "UTF-8"};
src = iso_source_new_ecma119(volset, &opts);
while (src->read(src, buf, 2048) == 2048) {
fwrite(buf, 1, 2048, fd);
}
fclose(fd);
return 0;
}

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,28 @@
#include "test.h"
static void create_test_suite()
{
add_util_suite();
add_tree_suite();
add_exclude_suite();
add_file_hashtable_suite();
add_ecma119_tree_suite();
add_volume_suite();
}
int main(int argc, char **argv)
{
CU_pSuite pSuite = NULL;
/* initialize the CUnit test registry */
if (CUE_SUCCESS != CU_initialize_registry())
return CU_get_error();
create_test_suite();
/* Run all tests using the console interface */
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
CU_cleanup_registry();
return CU_get_error();
}

View File

@ -0,0 +1,24 @@
#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();
#endif /*TEST_H_*/

View File

@ -0,0 +1,34 @@
/*
* Unit test for ecma119_tree.h
*/
//FIXME not implemented yet!!
#include "libisofs.h"
#include "tree.h"
#include "test.h"
//#include "ecma119_tree.h"
/*
* Also including C file, testing internal functions
*/
//#include "ecma119_tree.c"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
static void test_calc_dirent_len()
{
}
void add_ecma119_tree_suite()
{
CU_pSuite pSuite = CU_add_suite("Ecma119TreeSuite", NULL, NULL);
//CU_add_test(pSuite, "test of calc_dirent_len()", test_calc_dirent_len);
}

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,248 @@
/*
* 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->path = "/tmp/filename";
file->sort_weight = 1;
iso = iso_file_new(file);
CU_ASSERT_PTR_NOT_NULL(iso);
CU_ASSERT_STRING_EQUAL(iso->path, "/tmp/filename");
CU_ASSERT_EQUAL(iso->size, 12);
CU_ASSERT_EQUAL(iso->ino, 0);
CU_ASSERT_EQUAL(iso->nlink, 1);
CU_ASSERT_EQUAL(iso->sort_weight, 1);
CU_ASSERT_EQUAL(iso->real_dev, 15);
CU_ASSERT_EQUAL(iso->real_ino, 204);
}
static void test_add_lookup()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(1);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_TRUE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
CU_ASSERT_EQUAL(table->count, 1);
iso2 = iso_file_table_lookup(table, file1);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso1);
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "fileName2";
file2->node.attrib.st_dev = 152;
file2->node.attrib.st_ino = 2042;
file2->path = "/tmp/filename2";
iso3 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 1);
CU_ASSERT_EQUAL(table->count, 2);
/* treat to add the same file again */
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL(table->count, 2);
iso2 = iso_file_table_lookup(table, file1);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso1);
iso2 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NOT_NULL(iso2);
CU_ASSERT_PTR_EQUAL(iso2, iso3);
iso3 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
CU_ASSERT_EQUAL(table->count, 2);
iso_file_table_clear(table);
CU_ASSERT_EQUAL(table->count, 0);
iso2 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NULL(iso2);
free( file1 );
free( file2 );
free( table );
}
static void test_cache_inodes()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(1);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_TRUE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
/* another file, different but with the same inode id */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "another file";
file2->node.attrib.st_dev = 15;
file2->node.attrib.st_ino = 204;
file2->path = "/tmp/another";
iso2 = iso_file_new(file2);
/* ensure it's not added again... */
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 0);
/* ...and the lookup returns the first */
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_EQUAL(iso1, iso3);
free(iso2);
free(file2);
/* and now a file with same inode but different device */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "different file";
file2->node.attrib.st_dev = 16; /* different dev id */
file2->node.attrib.st_ino = 204;
file2->path = "/tmp/different";
iso2 = iso_file_new(file2);
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 1);
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_NOT_EQUAL(iso3, iso1);
CU_ASSERT_PTR_EQUAL(iso3, iso2);
iso_file_table_clear(table);
free( file1 );
free( file2 );
free( table );
}
static void test_no_cache_inodes()
{
struct iso_file_table *table;
struct iso_tree_node_file *file1;
struct iso_tree_node_file *file2;
struct iso_tree_node_file *file3;
struct iso_file *iso1;
struct iso_file *iso2;
struct iso_file *iso3;
int r;
table = iso_file_table_new(0);
CU_ASSERT_PTR_NOT_NULL( table );
CU_ASSERT_FALSE( table->cache_inodes );
CU_ASSERT_EQUAL(table->count, 0);
file1 = calloc(1, sizeof(struct iso_tree_node_file) );
file1->node.name = "fileName";
file1->node.attrib.st_dev = 15;
file1->node.attrib.st_ino = 204;
file1->path = "/tmp/filename";
iso1 = iso_file_new(file1);
r = iso_file_table_add_file(table, iso1);
CU_ASSERT_EQUAL(r, 1);
/* another file, different but with the same inode id */
file2 = calloc(1, sizeof(struct iso_tree_node_file) );
file2->node.name = "another file";
file2->node.attrib.st_dev = 15;
file2->node.attrib.st_ino = 204;
file2->path = "/tmp/another";
iso2 = iso_file_new(file2);
/* ensure is added */
r = iso_file_table_add_file(table, iso2);
CU_ASSERT_EQUAL(r, 1);
iso3 = iso_file_table_lookup(table, file2);
CU_ASSERT_PTR_EQUAL(iso3, iso2);
/* and now a file with same inode and path */
file3 = calloc(1, sizeof(struct iso_tree_node_file) );
file3->node.name = "different file";
file3->node.attrib.st_dev = 15;
file3->node.attrib.st_ino = 204;
file3->path = "/tmp/filename";
iso3 = iso_file_new(file3);
r = iso_file_table_add_file(table, iso3);
CU_ASSERT_EQUAL(r, 0);
iso3 = iso_file_table_lookup(table, file3);
CU_ASSERT_PTR_EQUAL(iso3, iso1);
iso_file_table_clear(table);
free(file1);
free(file2);
free(file3);
free(table);
}
void add_file_hashtable_suite()
{
CU_pSuite pSuite = CU_add_suite("FileHashtableSuite", NULL, NULL);
CU_add_test(pSuite, "test of iso_file_new()", test_iso_file_new);
CU_add_test(pSuite, "test of add and lookup", test_add_lookup);
CU_add_test(pSuite, "test with cache_inodes", test_cache_inodes);
CU_add_test(pSuite, "test without cache_inodes", test_no_cache_inodes);
}

View File

@ -0,0 +1,395 @@
/*
* Unit test for tree.h
*/
#include "libisofs.h"
#include "tree.h"
#include "test.h"
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
static void test_new_root() {
struct iso_tree_node_dir *root;
root = iso_tree_new_root();
CU_ASSERT_PTR_NOT_NULL(root);
CU_ASSERT_EQUAL(root->nchildren, 0);
CU_ASSERT_PTR_NULL(root->children);
CU_ASSERT_PTR_NULL(root->node.parent);
CU_ASSERT_PTR_NULL(root->node.name);
CU_ASSERT(S_ISDIR(root->node.attrib.st_mode) );
}
static void test_add_dir() {
struct iso_tree_node_dir *root;
struct iso_tree_node_dir *dir;
root = iso_tree_new_root();
CU_ASSERT_PTR_NOT_NULL(root);
dir = iso_tree_add_dir(root, "New dir name");
CU_ASSERT_PTR_NOT_NULL(root);
CU_ASSERT_PTR_NOT_NULL(dir);
CU_ASSERT_PTR_EQUAL(dir->node.parent, root);
CU_ASSERT_EQUAL(root->nchildren, 1);
CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)dir);
CU_ASSERT_STRING_EQUAL( dir->node.name, "New dir name");
CU_ASSERT( S_ISDIR(dir->node.attrib.st_mode) );
iso_tree_free((struct iso_tree_node *)root);
}
static void test_add_file() {
struct iso_tree_node_dir *root;
struct iso_tree_node_file *file;
root = iso_tree_new_root();
file = (struct iso_tree_node_file *)
iso_tree_add_file(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(root);
CU_ASSERT_PTR_NOT_NULL(file);
CU_ASSERT_PTR_EQUAL(file->node.parent, root);
CU_ASSERT_EQUAL(root->nchildren, 1);
CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)file);
CU_ASSERT_STRING_EQUAL( file->node.name, "README" );
CU_ASSERT_STRING_EQUAL( file->path, "/tmp/libisofs_test/README" );
CU_ASSERT( S_ISREG(file->node.attrib.st_mode) );
iso_tree_free((struct iso_tree_node *)root);
}
static void test_add_symlink() {
struct iso_tree_node_dir *root;
struct iso_tree_node *lnk;
root = iso_tree_new_root();
lnk = iso_tree_add_symlink(root, "read", "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(root);
CU_ASSERT_PTR_NOT_NULL(lnk);
CU_ASSERT_PTR_EQUAL(lnk->parent, root);
CU_ASSERT_EQUAL(root->nchildren, 1);
CU_ASSERT_PTR_EQUAL(root->children[0], (struct iso_tree_node *)lnk);
CU_ASSERT_STRING_EQUAL( lnk->name, "read");
CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)lnk)->dest,
"/tmp/libisofs_test/README" );
CU_ASSERT( S_ISLNK(lnk->attrib.st_mode) );
iso_tree_free((struct iso_tree_node *)root);
}
static void test_add_node() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
root = iso_tree_new_root();
/* test addition of a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_PTR_EQUAL(node->parent, root);
CU_ASSERT_EQUAL(root->nchildren, 1);
CU_ASSERT_PTR_EQUAL(root->children[0], node);
CU_ASSERT_STRING_EQUAL( node->name, "dir1");
CU_ASSERT( ISO_ISDIR(node) );
CU_ASSERT( S_ISDIR(node->attrib.st_mode) );
/* test addition of a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_PTR_EQUAL(node->parent, root);
CU_ASSERT_EQUAL(root->nchildren, 2);
CU_ASSERT_PTR_EQUAL(root->children[1], node);
CU_ASSERT( ISO_ISLNK(node) );
CU_ASSERT( S_ISLNK(node->attrib.st_mode) );
CU_ASSERT_STRING_EQUAL( node->name, "link to readme");
CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_symlink*)node)->dest,
"/tmp/libisofs_test/README" );
/* test addition of a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_PTR_EQUAL(node->parent, root);
CU_ASSERT_EQUAL(root->nchildren, 3);
CU_ASSERT_PTR_EQUAL(root->children[2], node);
CU_ASSERT( S_ISREG(node->attrib.st_mode) );
CU_ASSERT( ISO_ISREG(node) );
CU_ASSERT_STRING_EQUAL( node->name, "README" );
CU_ASSERT_STRING_EQUAL( ((struct iso_tree_node_file *) node)->path,
"/tmp/libisofs_test/README" );
/* test no exiting file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/THISNOTEXIST");
CU_ASSERT_PTR_NULL(node);
CU_ASSERT_EQUAL(libisofs_errno, NO_FILE);
CU_ASSERT_EQUAL(root->nchildren, 3);
/* test no valid file */
node = iso_tree_add_node(root, "/dev/zero");
CU_ASSERT_PTR_NULL(node);
CU_ASSERT_EQUAL(libisofs_errno, UNEXPECTED_FILE_TYPE);
CU_ASSERT_EQUAL(root->nchildren, 3);
/* test no read perm file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/no_read");
CU_ASSERT_PTR_NULL(node);
CU_ASSERT_EQUAL(libisofs_errno, NO_READ_ACCESS);
CU_ASSERT_EQUAL(root->nchildren, 3);
iso_tree_free((struct iso_tree_node *)root);
}
static void test_radd_dir() {
struct iso_tree_node_dir *root;
struct iso_tree_node_dir *child;
struct iso_tree_node_file *file;
struct iso_tree_radd_dir_behavior behavior = {0,0,0};
//TODO write really full test
root = iso_tree_new_root();
CU_ASSERT_PTR_NOT_NULL(root);
iso_tree_radd_dir(root, "/tmp/libisofs_test", &behavior);
/* test _root_ children */
/*
child = (struct iso_tree_node_dir *)root->children[0];
CU_ASSERT( S_ISDIR(child->node.attrib.st_mode) );
CU_ASSERT_EQUAL( child->nchildren, 2);
CU_ASSERT_STRING_EQUAL( child->node.name, "dir1" );
child = (struct iso_tree_node_dir *)root->children[1];
CU_ASSERT( S_ISDIR(child->node.attrib.st_mode) );
CU_ASSERT_EQUAL( child->nchildren, 0);
CU_ASSERT_STRING_EQUAL( child->node.name, "dir2" );
file = (struct iso_tree_node_file *)root->children[2];
CU_ASSERT( S_ISREG(file->node.attrib.st_mode) );
CU_ASSERT_STRING_EQUAL( file->node.name, "README" );
*/
//iso_tree_print( (struct iso_tree_node *)root, 4 );
}
static void test_set_name() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
root = iso_tree_new_root();
/* test on a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_STRING_EQUAL( node->name, "dir1");
iso_tree_node_set_name(node, "newname");
CU_ASSERT_STRING_EQUAL( node->name, "newname");
/* test on a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_STRING_EQUAL( node->name, "link to readme");
iso_tree_node_set_name(node, "new link name");
CU_ASSERT_STRING_EQUAL( node->name, "new link name");
/* test on a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_STRING_EQUAL( node->name, "README" );
iso_tree_node_set_name(node, "new file name");
CU_ASSERT_STRING_EQUAL( node->name, "new file name");
iso_tree_free((struct iso_tree_node *)root);
}
static void test_set_hidden() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
root = iso_tree_new_root();
/* test on a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->hide_flags, 0);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
/* test on a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->hide_flags, 0);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
/* test on a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->hide_flags, 0);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_JOLIET);
iso_tree_node_set_hidden(node, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
CU_ASSERT_EQUAL(node->hide_flags, LIBISO_HIDE_ON_RR|LIBISO_HIDE_ON_JOLIET);
iso_tree_free((struct iso_tree_node *)root);
}
static void test_set_gid() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
gid_t mygid = getgid();
root = iso_tree_new_root();
/* test on a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_gid, mygid);
iso_tree_node_set_gid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_gid, 1234);
/* test on a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_gid, mygid);
iso_tree_node_set_gid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_gid, 1234);
/* test on a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_gid, mygid);
iso_tree_node_set_gid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_gid, 1234);
iso_tree_free((struct iso_tree_node *)root);
}
static void test_set_uid() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
uid_t myuid = getuid();
root = iso_tree_new_root();
/* test on a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_uid, myuid);
iso_tree_node_set_uid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_uid, 1234);
/* test on a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_uid, myuid);
iso_tree_node_set_uid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_uid, 1234);
/* test on a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_uid, myuid);
iso_tree_node_set_uid(node, 1234);
CU_ASSERT_EQUAL(node->attrib.st_uid, 1234);
//TODO
iso_tree_free((struct iso_tree_node *)root);
}
static void test_set_permissions() {
struct iso_tree_node_dir *root;
struct iso_tree_node *node;
root = iso_tree_new_root();
/* test on a dir */
node = iso_tree_add_node(root, "/tmp/libisofs_test/dir1");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0755);
iso_tree_node_set_permissions(node, 0777);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0777);
iso_tree_node_set_permissions(node, 0744);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0744);
iso_tree_node_set_permissions(node, 0411);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFDIR | 0411);
/* test on a link */
node = iso_tree_add_node(root, "/tmp/libisofs_test/link to readme");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0777);
iso_tree_node_set_permissions(node, 0555);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0555);
iso_tree_node_set_permissions(node, 0744);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0744);
iso_tree_node_set_permissions(node, 0411);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFLNK | 0411);
/* test on a file */
node = iso_tree_add_node(root, "/tmp/libisofs_test/README");
CU_ASSERT_PTR_NOT_NULL(node);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0555);
iso_tree_node_set_permissions(node, 0777);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0777);
iso_tree_node_set_permissions(node, 0744);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0744);
iso_tree_node_set_permissions(node, 0411);
CU_ASSERT_EQUAL(node->attrib.st_mode, S_IFREG | 0411);
iso_tree_free((struct iso_tree_node *)root);
}
static void test_set_sort_weight() {
struct iso_tree_node_dir *root;
struct iso_tree_node_dir *dir;
struct iso_tree_node_file *file;
root = iso_tree_new_root();
dir = iso_tree_add_dir(root, "New dir name");
CU_ASSERT_PTR_NOT_NULL(dir);
file = (struct iso_tree_node_file *)
iso_tree_add_file(dir, "/tmp/libisofs_test/README");
CU_ASSERT_EQUAL(file->sort_weight, 0);
iso_tree_node_set_sort_weight((struct iso_tree_node *) file, 15);
CU_ASSERT_EQUAL(file->sort_weight, 15);
iso_tree_node_set_sort_weight((struct iso_tree_node *) file, -15);
CU_ASSERT_EQUAL(file->sort_weight, -15);
/* changes to dir involve update files inside it */
iso_tree_node_set_sort_weight((struct iso_tree_node *) dir, 28);
CU_ASSERT_EQUAL(file->sort_weight, 28);
iso_tree_free((struct iso_tree_node *)root);
}
void add_tree_suite()
{
CU_pSuite pSuite = CU_add_suite("TreeSuite", NULL, NULL);
CU_add_test(pSuite, "test of iso_tree_new_root()", test_new_root);
CU_add_test(pSuite, "test of iso_tree_add_dir()", test_add_dir);
CU_add_test(pSuite, "test of iso_tree_add_file()", test_add_file);
CU_add_test(pSuite, "test of iso_tree_add_symlink()", test_add_symlink);
CU_add_test(pSuite, "test of iso_tree_add_node()", test_add_node);
CU_add_test(pSuite, "test of iso_tree_radd_dir()", test_radd_dir);
CU_add_test(pSuite, "test of iso_tree_node_set_name()", test_set_name);
CU_add_test(pSuite, "test of iso_tree_node_set_hidden()", test_set_hidden);
CU_add_test(pSuite, "test of iso_tree_node_set_gid()", test_set_gid);
CU_add_test(pSuite, "test of iso_tree_node_set_uid()", test_set_uid);
CU_add_test(pSuite, "test of iso_tree_node_set_permissions()", test_set_permissions);
CU_add_test(pSuite, "test of iso_tree_node_set_sort_weight()", test_set_sort_weight);
}

View File

@ -0,0 +1,265 @@
/*
* Unit test for util.h
*
* This test utiliy functions
*
*/
#include "test.h"
#include "util.h"
static void test_div_up()
{
CU_ASSERT_EQUAL( div_up(1, 2), 1 );
CU_ASSERT_EQUAL( div_up(2, 2), 1 );
CU_ASSERT_EQUAL( div_up(0, 2), 0 );
CU_ASSERT_EQUAL( div_up(-1, 2), 0 );
CU_ASSERT_EQUAL( div_up(3, 2), 2 );
}
static void test_round_up()
{
CU_ASSERT_EQUAL( round_up(1, 2), 2 );
CU_ASSERT_EQUAL( round_up(2, 2), 2 );
CU_ASSERT_EQUAL( round_up(0, 2), 0 );
CU_ASSERT_EQUAL( round_up(-1, 2), 0 );
CU_ASSERT_EQUAL( round_up(3, 2), 4 );
CU_ASSERT_EQUAL( round_up(15, 7), 21 );
CU_ASSERT_EQUAL( round_up(13, 7), 14 );
CU_ASSERT_EQUAL( round_up(14, 7), 14 );
}
static void test_iso_lsb_msb()
{
uint8_t buf[4];
uint32_t num;
num = 0x01020304;
iso_lsb(buf, num, 4);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
CU_ASSERT_EQUAL( buf[2], 0x02 );
CU_ASSERT_EQUAL( buf[3], 0x01 );
iso_msb(buf, num, 4);
CU_ASSERT_EQUAL( buf[0], 0x01 );
CU_ASSERT_EQUAL( buf[1], 0x02 );
CU_ASSERT_EQUAL( buf[2], 0x03 );
CU_ASSERT_EQUAL( buf[3], 0x04 );
iso_lsb(buf, num, 2);
CU_ASSERT_EQUAL( buf[0], 0x04 );
CU_ASSERT_EQUAL( buf[1], 0x03 );
iso_msb(buf, num, 2);
CU_ASSERT_EQUAL( buf[0], 0x03 );
CU_ASSERT_EQUAL( buf[1], 0x04 );
}
static void test_iso_1_dirid()
{
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("DIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwithbigname", "UTF-8"), "DIRWITHB");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dirwith8", "UTF-8"), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("dir.1", "UTF-8"), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_1_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM_");
}
static void test_iso_2_dirid()
{
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("DIR1", "UTF-8"), "DIR1" );
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwithbigname", "UTF-8"), "DIRWITHBIGNAME");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dirwith8", "UTF-8"), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("dir.1", "UTF-8"), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("4f<0KmM::xcvf", "UTF-8"), "4F_0KMM__XCVF");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with 31 characters ok", "UTF-8"), "DIRECTORY_WITH_31_CHARACTERS_OK");
CU_ASSERT_STRING_EQUAL( iso_2_dirid("directory with more than 31 characters", "UTF-8"), "DIRECTORY_WITH_MORE_THAN_31_CHA");
}
static void test_iso_r_dirid()
{
int flag;
/* 1. only ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "DIRWITHBIGNAME");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "DIRWITH8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "DIR_1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4F_0KMM__XCVF");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "DIRECTORY_WITH_31_CHARACTERS_OK");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "DIRECTORY_WITH_MORE_THAN_37_CHARACTER");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "DIRECTORY_WITH_JUST_37_CHARACTERS__OK");
/* 2. only ECMA119_RELAXED_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 cha");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characte");
/* 3. both ECMA119_RELAXED_FILENAMES and ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES | ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir1", "UTF-8", flag), "dir1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dIR1", "UTF-8", flag), "dIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("DIR1", "UTF-8", flag), "DIR1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwithbigname", "UTF-8", flag), "dirwithbigname");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dirwith8", "UTF-8", flag), "dirwith8");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("dir.1", "UTF-8", flag), "dir.1");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("4f<0KmM::xcvf", "UTF-8", flag), "4f<0KmM::xcvf");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with 31 characters ok", "UTF-8", flag), "directory with 31 characters ok");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with more than 37 characters", "UTF-8", flag), "directory with more than 37 character");
CU_ASSERT_STRING_EQUAL( iso_r_dirid("directory with just 37 characters ok", "UTF-8", flag), "directory with just 37 characters ok");
}
static void test_iso_1_fileid()
{
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fILe1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid(".EXT", "UTF-8"), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.EXt", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename", "UTF-8"), "BIGFILEN.;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.ext", "UTF-8"), "BIGFILEN.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.e", "UTF-8"), "BIGFILEN.E;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.bigext", "UTF-8"), "FILE.BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid(".bigext", "UTF-8"), ".BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("bigfilename.bigext", "UTF-8"), "BIGFILEN.BIG;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.<:a", "UTF-8"), "FILE.__A;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_1_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_.EX3;1");
}
static void test_iso_2_fileid()
{
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fILe1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE1", "UTF-8"), "FILE1.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".EXT", "UTF-8"), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fiLE.ext", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.EXt", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("FILE.EXT", "UTF-8"), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename", "UTF-8"), "BIGFILENAME.;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.ext", "UTF-8"), "BIGFILENAME.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.e", "UTF-8"), "BIGFILENAME.E;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("31 characters filename.extensio", "UTF-8"), "31_CHARACTERS_FILENAME.EXTENSIO;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("32 characters filename.extension", "UTF-8"), "32_CHARACTERS_FILENAME.EXTENSIO;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("more than 30 characters filename.extension", "UTF-8"), "MORE_THAN_30_CHARACTERS_FIL.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.bigext", "UTF-8"), "FILE.BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".bigext", "UTF-8"), ".BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("bigfilename.bigext", "UTF-8"), "BIGFILENAME.BIGEXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.ext", "UTF-8"), "FILE__A.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.<:a", "UTF-8"), "FILE.__A;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file<:a.--a", "UTF-8"), "FILE__A.__A;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2", "UTF-8"), "FILE_EX1.EX2;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("file.ex1.ex2.ex3", "UTF-8"), "FILE_EX1_EX2.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid("fil.ex1.ex2.ex3", "UTF-8"), "FIL_EX1_EX2.EX3;1");
CU_ASSERT_STRING_EQUAL( iso_2_fileid(".file.bigext", "UTF-8"), "_FILE.BIGEXT;1");
}
static void test_iso_r_fileid()
{
int flag;
/* 1. only ECMA119_OMIT_VERSION_NUMBERS */
flag = ECMA119_OMIT_VERSION_NUMBERS;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXT");
/* 2. only ECMA119_37_CHAR_FILENAMES */
flag = ECMA119_37_CHAR_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "BIGFILENAME.");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "BIGFILENAME.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "BIGFILENAME.E");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "FILE.BIGEXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31_CHARACTERS_FILENAME.EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "IT_S_37_CHARACTERS_FILENAME.EXTENSION");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("more than 37 characters filename.extension", "UTF-8", flag), "MORE_THAN_37_CHARACTERS_FILENAME.EXTE");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "FILE.THIS_IS_A_37_CHARS_LEN_EXTENSION");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a very very big extension", "UTF-8", flag), "FILE.THIS_IS_A_VERY_VERY_BIG_EXTENSIO");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "FIL_EX1_EX2.EX3");
/* 3. only ECMA119_RELAXED_FILENAMES */
flag = ECMA119_RELAXED_FILENAMES;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3;1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a;1");
/* 3. ECMA119_RELAXED_FILENAMES and ECMA119_OMIT_VERSION_NUMBERS*/
flag = ECMA119_RELAXED_FILENAMES | ECMA119_OMIT_VERSION_NUMBERS;
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file1", "UTF-8", flag), "file1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fILe1", "UTF-8", flag), "fILe1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE1", "UTF-8", flag), "FILE1");
CU_ASSERT_STRING_EQUAL( iso_r_fileid(".EXT", "UTF-8", flag), ".EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.ext", "UTF-8", flag), "file.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fiLE.ext", "UTF-8", flag), "fiLE.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.EXt", "UTF-8", flag), "file.EXt");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("FILE.EXT", "UTF-8", flag), "FILE.EXT");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename", "UTF-8", flag), "bigfilename");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.ext", "UTF-8", flag), "bigfilename.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("bigfilename.e", "UTF-8", flag), "bigfilename.e");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.bigext", "UTF-8", flag), "file.bigext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("31 characters filename.extensio", "UTF-8", flag), "31 characters filename.extensio");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("it's 37 characters filename.extension", "UTF-8", flag), "it's 37 characters filename.ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.this is a 37 chars len extension", "UTF-8", flag), "file.this is a 37 chars len ext");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("fil.ex1.ex2.ex3", "UTF-8", flag), "fil.ex1.ex2.ex3");
CU_ASSERT_STRING_EQUAL( iso_r_fileid("file.<:a", "UTF-8", flag), "file.<:a");
}
void add_util_suite()
{
CU_pSuite pSuite = CU_add_suite("UtilSuite", NULL, NULL);
CU_add_test(pSuite, "test of div_up()", test_div_up);
CU_add_test(pSuite, "test of round_up()", test_round_up);
CU_add_test(pSuite, "test of iso_lsb_msb()", test_iso_lsb_msb);
CU_add_test(pSuite, "test of iso_1_dirid()", test_iso_1_dirid);
CU_add_test(pSuite, "test of iso_2_dirid()", test_iso_2_dirid);
CU_add_test(pSuite, "test of iso_r_dirid()", test_iso_r_dirid);
CU_add_test(pSuite, "test of iso_1_fileid()", test_iso_1_fileid);
CU_add_test(pSuite, "test of iso_2_fileid()", test_iso_2_fileid);
CU_add_test(pSuite, "test of iso_r_fileid()", test_iso_r_fileid);
}

View File

@ -0,0 +1,238 @@
/*
* 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_set_publisher_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "publisher_id" );
char *pubid = "new publisher id";
iso_volume_set_publisher_id(volume, pubid);
CU_ASSERT_STRING_EQUAL( volume->publisher_id, "new publisher id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->publisher_id, pubid );
iso_volume_free(volume);
}
static void test_iso_volume_set_data_preparer_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "data_preparer_id" );
char *dpid = "new data preparer id";
iso_volume_set_data_preparer_id(volume, dpid);
CU_ASSERT_STRING_EQUAL( volume->data_preparer_id, "new data preparer id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->data_preparer_id, dpid );
iso_volume_free(volume);
}
static void test_iso_volume_set_system_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->system_id);
char *sysid = "new system id";
iso_volume_set_system_id(volume, sysid);
CU_ASSERT_STRING_EQUAL( volume->system_id, "new system id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->system_id, sysid );
iso_volume_free(volume);
}
static void test_iso_volume_set_application_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->application_id);
char *appid = "new application id";
iso_volume_set_application_id(volume, appid);
CU_ASSERT_STRING_EQUAL( volume->application_id, "new application id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->application_id, appid );
iso_volume_free(volume);
}
static void test_iso_volume_set_abstract_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->abstract_file_id);
char *absid = "new abstract id";
iso_volume_set_abstract_file_id(volume, absid);
CU_ASSERT_STRING_EQUAL( volume->abstract_file_id, "new abstract id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->abstract_file_id, absid );
iso_volume_free(volume);
}
static void test_iso_volume_set_biblio_file_id()
{
struct iso_volume *volume;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
CU_ASSERT_PTR_NULL(volume->biblio_file_id);
char *bibid = "new biblio id";
iso_volume_set_biblio_file_id(volume, bibid);
CU_ASSERT_STRING_EQUAL( volume->biblio_file_id, "new biblio id" );
/* check string was strdup'ed */
CU_ASSERT_PTR_NOT_EQUAL( volume->biblio_file_id, bibid );
iso_volume_free(volume);
}
static void test_iso_volset_new()
{
struct iso_volume *volume;
struct iso_volset *volset;
volume = iso_volume_new("volume_id", "publisher_id", "data_preparer_id");
volset = iso_volset_new(volume, "volset_id");
CU_ASSERT_PTR_NOT_NULL(volset);
CU_ASSERT_EQUAL(volset->refcount, 1);
CU_ASSERT_EQUAL(volset->volset_size, 1);
CU_ASSERT_PTR_NOT_NULL(volset->volume);
CU_ASSERT_PTR_NOT_NULL(volset->volume[0]);
CU_ASSERT_PTR_EQUAL(volset->volume[0], volume);
CU_ASSERT_STRING_EQUAL( volset->volset_id, "volset_id" );
iso_volset_free(volset);
}
void add_volume_suite()
{
CU_pSuite pSuite = CU_add_suite("VolumeSuite", NULL, NULL);
CU_add_test(pSuite, "test of iso_volume_new()", test_iso_volume_new);
CU_add_test(pSuite, "test of iso_volume_new_with_root()", test_iso_volume_new_with_root);
CU_add_test(pSuite, "test of iso_volume_get_root()", test_iso_volume_get_root);
CU_add_test(pSuite, "test of iso_volume_set_volume_id()", test_iso_volume_set_volume_id);
CU_add_test(pSuite, "test of iso_volume_set_publisher_id()", test_iso_volume_set_publisher_id);
CU_add_test(pSuite, "test of iso_volume_set_data_preparer_id()", test_iso_volume_set_data_preparer_id);
CU_add_test(pSuite, "test of iso_volume_set_system_id()", test_iso_volume_set_system_id);
CU_add_test(pSuite, "test of iso_volume_set_application_id()", test_iso_volume_set_application_id);
CU_add_test(pSuite, "test of iso_volume_set_abstract_file_id()", test_iso_volume_set_abstract_file_id);
CU_add_test(pSuite, "test of iso_volume_set_biblio_file_id()", test_iso_volume_set_biblio_file_id);
CU_add_test(pSuite, "test of iso_volset_new()", test_iso_volset_new);
}

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

@ -18,6 +18,12 @@ static size_t calc_dirent_len(struct ecma119_tree_node *n)
return ret;
}
/**
* Replace the file permissions and user/group id of an ECMA-119 node.
* This is used when a replace mode is selected, i.e., when we want to
* create a disc where the mode of each file or directory will be
* different than the mode in the original source.
*/
static void
replace_node_mode(struct ecma119_write_target *t, struct stat *st)
{
@ -65,6 +71,19 @@ replace_node_mode(struct ecma119_write_target *t, struct stat *st)
}
}
/**
* Creates a new ECMA-119 node from the given iso tree node, and initializes
* the fields that are common to all kind of nodes (dir, reg file, symlink...).
*
* @param t
* The options for the ECMA-119 tree that is being created
* @param parent
* The parent of the node, or NULL if it's the root.
* @param iso
* The node from which this function creates a ECMA-119 node
* @return
* The created node.
*/
static struct ecma119_tree_node*
create_ecma119_node(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -92,13 +111,22 @@ create_ecma119_node(struct ecma119_write_target *t,
iso_name(iso->name, t->input_charset)
) : NULL;
ret->dirent_len = calc_dirent_len(ret);
/* iso node keeps the same file attribs as the original file. */
ret->attrib = iso->attrib;
/*
* When using RR extension and replace mode, we will replace the
* permissions and uid/gid of each file with those previously selected
* by the user.
*/
if ( t->rockridge && t->replace_mode )
replace_node_mode(t, &ret->attrib);
if (!iso->name)
ret->full_name = NULL;
else if ( strcmp(t->input_charset,t->ouput_charset) )
/* convert the file name charset */
ret->full_name = convert_str(iso->name, t->input_charset,
t->ouput_charset);
else
@ -108,6 +136,10 @@ create_ecma119_node(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node representing a directory from a iso directory
* node.
*/
static struct ecma119_tree_node*
create_dir(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -127,6 +159,10 @@ create_dir(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node representing a regular file from a iso file
* node.
*/
static struct ecma119_tree_node*
create_file(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -143,6 +179,10 @@ create_file(struct ecma119_write_target *t,
/* get iso_file struct */
file = iso_file_table_lookup(t->file_table, iso);
if ( file == NULL ) {
/*
* If the file is not already added to the disc, we add it now
* to the file table, and get a new inode number for it.
*/
file = iso_file_new(iso);
iso_file_table_add_file(t->file_table, file);
file->ino = ++t->ino;
@ -157,6 +197,12 @@ create_file(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node representing a placeholder for a relocated
* dir.
*
* See IEEE P1282, section 4.1.5 for details
*/
static struct ecma119_tree_node*
create_placeholder(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -191,6 +237,10 @@ create_placeholder(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node representing a symbolic link from a iso symlink
* node.
*/
static struct ecma119_tree_node*
create_symlink(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -212,6 +262,14 @@ create_symlink(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node representing a boot catalog. This is like a
* regular file, but its contents are taken from a El-Torito boot catalog,
* and not from a file in the local filesystem.
*
* See "El Torito" Bootable CD-ROM Format Specification Version 1.0 for
* more details.
*/
static struct ecma119_tree_node*
create_boot_catalog(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -239,6 +297,11 @@ create_boot_catalog(struct ecma119_write_target *t,
return ret;
}
/**
* Create a new ECMA-119 node that corresponds to the given iso tree node.
* If that node is a dir, this function recurses over all their children,
* thus creating a ECMA-119 tree whose root is the given iso dir.
*/
static struct ecma119_tree_node*
create_tree(struct ecma119_write_target *t,
struct ecma119_tree_node *parent,
@ -316,6 +379,12 @@ max_child_name_len(struct ecma119_tree_node *root)
return ret;
}
/**
* Relocates a directory, as specified in Rock Ridge Specification
* (see IEEE P1282, section 4.1.5). This is needed when the number of levels
* on a directory hierarchy exceeds 8, or the length of a path is higher
* than 255 characters, as specified in ECMA-119, section 6.8.2.1
*/
static void
reparent(struct ecma119_tree_node *child,
struct ecma119_tree_node *parent)
@ -350,6 +419,8 @@ reparent(struct ecma119_tree_node *child,
* Reorder the tree, if necessary, to ensure that
* - the depth is at most 8
* - each path length is at most 255 characters
* This restriction is imposed by ECMA-119 specification (see ECMA-119,
* 6.8.2.1).
*/
static void
reorder_tree(struct ecma119_write_target *t,
@ -400,6 +471,9 @@ reorder_tree(struct ecma119_write_target *t,
}
}
/**
* Compare the iso name of two ECMA-119 nodes
*/
static int
cmp_node(const void *f1, const void *f2)
{
@ -408,6 +482,10 @@ cmp_node(const void *f1, const void *f2)
return strcmp(f->iso_name, g->iso_name);
}
/**
* Sorts a the children of each directory in the ECMA-119 tree represented
* by \p root, acording to the order specified in ECMA-119, section 9.3.
*/
static void
sort_tree(struct ecma119_tree_node *root)
{
@ -470,6 +548,10 @@ mangle_name(char **name, int num_change, int level, int seq_num)
sprintf(*name, fmt, base, seq_num, ext);
}
/**
* Ensures that the ISO name of each children of the given dir is unique,
* changing some of them if needed.
*/
static void
mangle_all(struct ecma119_tree_node *dir)
{

View File

@ -14,6 +14,30 @@
#include <unistd.h>
#include <sys/stat.h>
/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
void
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
unsigned char *ER = malloc(182);
assert(dir->type == ECMA119_DIR);
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
susp_append(t, &dir->info.dir.self_susp, ER);
}
/* create a PX field from the permissions on the current node. */
uint8_t *rrip_make_PX(struct ecma119_write_target *t,
struct ecma119_tree_node *node)

View File

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

View File

@ -155,30 +155,6 @@ static void susp_add_ST(struct ecma119_write_target *t,
}
#endif
/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
void
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
{
unsigned char *ER = malloc(182);
assert(dir->type == ECMA119_DIR);
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
susp_append(t, &dir->info.dir.self_susp, ER);
}
/* calculate the location of the CE areas. Since CE areas don't need to be
* aligned to a block boundary, we contatenate all CE areas from a single
* directory and dump them immediately after all the directory records.

View File

@ -1,6 +1,10 @@
/* vim: set noet ts=8 sts=8 sw=8 : */
/** Functions and structures used for SUSP (IEEE 1281).
/**
* Functions and structures used for SUSP (IEEE 1281).
*
* Please refer to IEEE P1281 System Use Sharing Protocol, draft standard
* version 1.12 for more details.
*/
#ifndef __ISO_SUSP
@ -28,13 +32,23 @@ struct susp_info
* will go in a CE area. */
};
/**
* Add a CE System Use Entry to the given tree node. A "CE" is used to add
* a continuation area, where additional System Use Entry can be written.
* See IEEE P1281, section 5.1.
*/
void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *);
/* these next 2 are special because they don't modify the susp fields of the
* directory; they modify the susp fields of the
* "." entry in the directory. */
/**
* Add a SP System Use Entry to the "." entry of the directory. The SP provide
* an identifier that the SUSP is used within the volume. The SP shall be
* recorded in the "." entry of the root directory.
* See IEEE P1281, section 5.3 for more details.
*
* this is special because it doesn't modify the susp fields of the
* directory; it modifies the susp fields of the "." entry in the directory.
*/
void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *);
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
/** Once all the directories and files are laid out, recurse through the tree
* and finalize all SUSP CE entries. */