Compare commits
470 Commits
ZeroTwoTwo
...
ZeroThreeT
Author | SHA1 | Date | |
---|---|---|---|
113a8294ad | |||
86ac7a9754 | |||
537a8ee1d7 | |||
d95f777bd8 | |||
e8d210e15f | |||
6df2745652 | |||
09b5bd2a1f | |||
67ce096aeb | |||
cad7531058 | |||
8dccb584b4 | |||
50eba7ee31 | |||
494693cc63 | |||
05623cffc4 | |||
fed2af8c69 | |||
78f1cf835d | |||
d2283a5b4e | |||
8dd8ee4b9c | |||
6dfbbdf57f | |||
d57166f64b | |||
2799fe1b44 | |||
330e0e2d5f | |||
135d9d5016 | |||
db9f4a1d04 | |||
6b8ab52854 | |||
4e93de2cd2 | |||
a769f8aa87 | |||
e0a38be97b | |||
100de7b160 | |||
9f7e5dd6dd | |||
a9ac7ae480 | |||
2e75680ed7 | |||
c02dbe4eca | |||
f3a08f6812 | |||
e876f90e6c | |||
dde52c1971 | |||
f194aa8d5d | |||
b18b889750 | |||
0aafc18ac7 | |||
fa792f6a67 | |||
53a1e99675 | |||
bda139f390 | |||
ff7028d1fc | |||
fa350edaae | |||
55e6c0ba82 | |||
f5a350f2cc | |||
c079e09860 | |||
99f8e0eec5 | |||
3aec7fdeda | |||
814d8cc83d | |||
bc694d7457 | |||
66d70075ec | |||
8f4dd8580c | |||
7dd845a83b | |||
7b024f4a72 | |||
f1215f3295 | |||
8e17806405 | |||
fb98c0390e | |||
f418c49e63 | |||
a34e872511 | |||
69a07aeec6 | |||
7addf0d5b5 | |||
a001f96b21 | |||
4a26017f2a | |||
0f48910aff | |||
c68efb346c | |||
36f287d569 | |||
4d7e28c09f | |||
c1342195f6 | |||
1fd003c1ec | |||
36d475eaf9 | |||
9180a0036d | |||
f28ec96709 | |||
16baf48b2e | |||
caef0aa400 | |||
fefdbc646f | |||
44cd12a0a0 | |||
07cef29616 | |||
a426df57d7 | |||
a2f8174330 | |||
b6025c6e4a | |||
ceb5159770 | |||
f77dd3134b | |||
c381104505 | |||
d61f1279c1 | |||
0f34fb03a0 | |||
ef6212f4ec | |||
fcc9f8fe8e | |||
8877b36625 | |||
435eeea15f | |||
15af71d448 | |||
27262bde51 | |||
7bf228954c | |||
071feccd32 | |||
f1b817b520 | |||
e6cfff69eb | |||
5fc55ec164 | |||
7211f210dc | |||
fdd40d4492 | |||
6a69cfcf07 | |||
ab51d551d3 | |||
f6b751c4bd | |||
f23deebe80 | |||
b362f80d40 | |||
f634a52e86 | |||
441c2a32cd | |||
bee5484d46 | |||
342283ffea | |||
cb24c90f0c | |||
1824c065f0 | |||
9e4c2866a9 | |||
97fe858760 | |||
ca9d11c692 | |||
c05d6550cb | |||
6e9c399930 | |||
7c6895aec2 | |||
a9e4bc1dbd | |||
5f4e9d9cfb | |||
c4fe384b56 | |||
b2cf633bdc | |||
13ecca61bc | |||
6277678a8e | |||
cafad767a9 | |||
18ba02c2bb | |||
85ffe77a32 | |||
95bebfb814 | |||
1b68e65005 | |||
979e35d979 | |||
d4711abba8 | |||
d53114b723 | |||
a4846ef882 | |||
00afac796e | |||
42c0cc6dd4 | |||
221aee23d1 | |||
c3a5d3c7a5 | |||
a7610aa0c9 | |||
fb4a209a34 | |||
9ba4f5f3df | |||
745c8ca611 | |||
2f007fcb36 | |||
7e30dbfc5a | |||
1ce6ec4a3a | |||
750b897ef0 | |||
c6555dd8f5 | |||
c09d8e6da6 | |||
9d65aa1c40 | |||
59d3e7296b | |||
9a752c6130 | |||
ce27ad5159 | |||
1f3c3fe622 | |||
ff755e7204 | |||
4a2402171a | |||
8d54a56590 | |||
dae337de09 | |||
adaf3f20d7 | |||
de33492cde | |||
1b1ef5a31d | |||
b02b2a40e7 | |||
94848398c0 | |||
c1f49bab3b | |||
4778a57c53 | |||
ea923c391a | |||
0aec5f2981 | |||
59a8cfc0d4 | |||
99eeb27688 | |||
336c599741 | |||
f095e04165 | |||
409c086581 | |||
0f1d433d5b | |||
deed90b65e | |||
ab08d66af3 | |||
3acd08b34b | |||
d187efea4d | |||
ed5eb49b18 | |||
c9fc338174 | |||
8c6c78ed4e | |||
2dea8b0f60 | |||
0160a98e67 | |||
84b87f4eb5 | |||
4c70280527 | |||
178a0305c5 | |||
fcf9c50220 | |||
17bac86aae | |||
d9e502ee5f | |||
17fc8e7161 | |||
e405092ab0 | |||
c6d22a1eb0 | |||
8265834404 | |||
592b52df5e | |||
4d62e6003f | |||
77f44ce5a2 | |||
5ecd7455ac | |||
bb951e0538 | |||
1677d40e91 | |||
4c0d113956 | |||
ae338b8f2c | |||
d5eda47306 | |||
9a0442b60c | |||
c4c571f7e3 | |||
81c86161a7 | |||
003c72a396 | |||
13f77b4428 | |||
80f5923e90 | |||
165e83b692 | |||
e1b2beea4e | |||
bab1cf96c6 | |||
aafc0b0be2 | |||
915e24a0c2 | |||
7d7c19b628 | |||
f6ce7ca8ce | |||
e29f13980b | |||
90d0a6952c | |||
91ff41856b | |||
229328e869 | |||
ad2c080145 | |||
6869e12e4d | |||
0e3fb8d620 | |||
221aa948e4 | |||
9270f41c65 | |||
3d5cb04093 | |||
15df1d819c | |||
11f16316b1 | |||
56ab215db0 | |||
13214d9ac5 | |||
c67bd685a2 | |||
d20c4fee8c | |||
ba982ebf64 | |||
5bd74c3064 | |||
5cdc9db76c | |||
de02cc26f9 | |||
35a1a1f7d7 | |||
54d3702513 | |||
c3f9764e0a | |||
ab4096e720 | |||
26fa325ee4 | |||
314027631a | |||
6e09b56c29 | |||
08f541a7a8 | |||
26412b4793 | |||
9ea689d7e4 | |||
f3b994f845 | |||
46906c7f65 | |||
4daed7c4a9 | |||
ebb94ee212 | |||
18a592ad28 | |||
ba48efbc52 | |||
0513a4eb2f | |||
254852ee36 | |||
ef6d76c22d | |||
66c3ead5d6 | |||
908a6b336b | |||
373e401671 | |||
06d008d6c6 | |||
11c9444b6c | |||
ff284b3f51 | |||
628e935fe5 | |||
13641fcbae | |||
595b877a24 | |||
17d1d2e8a6 | |||
c1b3d3341e | |||
725b6515ff | |||
61a535f3ac | |||
7c312a33d9 | |||
b7d369dbb6 | |||
16cbb18f15 | |||
4f83818ff8 | |||
9e383a3b9e | |||
a9b7d8250e | |||
1ec94d9226 | |||
c1607eeac4 | |||
8927e3df9f | |||
7458223cc0 | |||
5a0b597e3f | |||
bf20758906 | |||
1bfd7b5a5f | |||
aa813303a4 | |||
9baf20e907 | |||
1947c3a25d | |||
6b22dadcdd | |||
4efccd15b3 | |||
496fb67c29 | |||
bc97bdfa71 | |||
68784d8703 | |||
10c01ebc83 | |||
6c22b98235 | |||
62bd5963d0 | |||
726cd2664d | |||
5378638ba7 | |||
347aa3fe0c | |||
c9478cc15e | |||
ce0d783e82 | |||
5057fe48ab | |||
fcc755a5b8 | |||
ab8127f980 | |||
dad50348db | |||
140ff9ade4 | |||
2a64a0d3b1 | |||
82983cd7c1 | |||
b31e882582 | |||
dd80b2fc88 | |||
3051fc1987 | |||
e54be83b27 | |||
448aac8880 | |||
db4522bbdc | |||
d7c34f05e4 | |||
b96fbd5bd8 | |||
25d5a2afe4 | |||
66515523c1 | |||
684cfd0cd2 | |||
a0e745c54a | |||
a239d54bd7 | |||
c98dd9b0a7 | |||
193d674852 | |||
15e37555ae | |||
d337722920 | |||
5af92d13fe | |||
114307a679 | |||
99ca953b1d | |||
b837cc134d | |||
d3ee0b72e4 | |||
6914e2f7e2 | |||
37017119b0 | |||
447d2365c7 | |||
ece61a1860 | |||
acc6bdd95c | |||
559811e8ef | |||
be66006d44 | |||
64f2e555d7 | |||
04a0b7e8fc | |||
4b98af6011 | |||
7d6e1b18e6 | |||
f95d7aa6aa | |||
0d26beb2ed | |||
f78cc40041 | |||
c51a93fd9a | |||
404067bfda | |||
efaa934a2e | |||
a4d7710063 | |||
3b57183e91 | |||
8f232d1133 | |||
e5ed5f1856 | |||
6f0643b7cf | |||
6971f3f96d | |||
b6a04cb493 | |||
4718ad99f9 | |||
af44cbccf2 | |||
8610c9bd1d | |||
84ecb57b47 | |||
fe7552e55d | |||
d9891ee17f | |||
13999f533e | |||
1477b9097f | |||
1f11d1c462 | |||
4dbae90e80 | |||
a8d9882280 | |||
82bddb6e4c | |||
8961d47fed | |||
401329f710 | |||
a6f394f626 | |||
362bcc9cc0 | |||
1e80ad2187 | |||
f8fd6a21f4 | |||
398d068820 | |||
e326e9aac9 | |||
fdfc4ebf36 | |||
cb42e2fcd9 | |||
1851f3f82a | |||
e423bda96f | |||
9d3f1292d6 | |||
6cdfb35c09 | |||
e28798d8a0 | |||
0710bbb4fb | |||
6ad8e2e251 | |||
170eb96356 | |||
1d570af0b5 | |||
5fd59b1858 | |||
86bfcea2b3 | |||
16086d5a7b | |||
ac6eed7aef | |||
6310bd0c74 | |||
be991c61d7 | |||
6c3f912561 | |||
96af620334 | |||
bd1577debd | |||
cf8aacb27d | |||
5d0b8265b2 | |||
f5603c7519 | |||
2403532242 | |||
988622c3b6 | |||
c6bd87af59 | |||
b202e3be5b | |||
d252d1fc9b | |||
a8921e4f59 | |||
ff0be0eeae | |||
91f2a231f3 | |||
16d3089ba8 | |||
b6831605b2 | |||
1fe7f68b43 | |||
3b16ef699f | |||
b3bea43074 | |||
5aa2729766 | |||
9c48ad4dff | |||
6dbcf6a8f4 | |||
22202a4301 | |||
5c45c2aa1b | |||
c70fc7215d | |||
b81aa81c61 | |||
29d630b141 | |||
886b8d1f81 | |||
927352c609 | |||
84ee76f564 | |||
70a14832b0 | |||
6127dade34 | |||
c430359a99 | |||
e66581f3b3 | |||
2d2495db29 | |||
0ee5e11a8f | |||
81f56e8bbd | |||
facd49dc4e | |||
bbcf7c269c | |||
12ad9100ff | |||
a27361b3b0 | |||
f2c87eae59 | |||
6f1a0c91d4 | |||
15b3f2627c | |||
1a69a09766 | |||
5038e2afb0 | |||
bcaf610800 | |||
6e01855f40 | |||
187fc90edf | |||
eadef2c51d | |||
81ceaf0cdd | |||
c9a3fd3140 | |||
e388a6396f | |||
8e73ee88e9 | |||
faaefaa4b8 | |||
8c0b17b382 | |||
b3c2e76975 | |||
3f7289d104 | |||
7e4f284f18 | |||
c1baf5c6d4 | |||
9d4b327adc | |||
b67c555b30 | |||
d0d0af1ba5 | |||
f580ef6637 | |||
7937cb5d9b | |||
bf5073e7c1 | |||
7ea38bbd31 | |||
1e70b67999 | |||
c5d849516c | |||
e8d71b321f | |||
db8dbbf2c3 | |||
0fe8ae2ef6 | |||
ff16978824 | |||
d47767021f | |||
7ad4f989d4 | |||
e4e671aa6e | |||
27e9e35916 | |||
ccf39cbab9 | |||
c4e6974c21 | |||
496e027f89 | |||
8ee6839513 | |||
c343d2f9b8 | |||
ba2b5aea0f | |||
600a2ebf02 | |||
4ade058cf7 | |||
8f201c0bd7 | |||
171fce7fc8 | |||
1d010c7c98 | |||
c4e484ff76 | |||
83b175c172 |
@ -1,2 +1,5 @@
|
|||||||
Joe Neeman
|
Joe Neeman
|
||||||
Philippe Rouquier
|
Philippe Rouquier
|
||||||
|
Gabriel Craciunescu
|
||||||
|
George Danchev
|
||||||
|
Jean-Francois Wauthy
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net>
|
Derek Foreman <derek@signalmarketing.com> and Ben Jansens <xor@orodu.net>
|
||||||
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
||||||
Mario Danic <mario.danic@gmail.com>, Thomas Schmitt <scdbackup@gmx.net>
|
Mario Danic <mario.danic@gmail.com>, Thomas Schmitt <scdbackup@gmx.net>
|
||||||
Copyright (C) 2006 Mario Danic, Thomas Schmitt
|
Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt
|
||||||
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
234
INSTALL
Normal file
234
INSTALL
Normal 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.
|
||||||
|
|
91
Makefile.am
91
Makefile.am
@ -1,17 +1,20 @@
|
|||||||
pkgconfigdir=$(libdir)/pkgconfig
|
pkgconfigdir=$(libdir)/pkgconfig
|
||||||
libincludedir=$(includedir)/libburn
|
libincludedir=$(includedir)/libburn
|
||||||
|
|
||||||
lib_LTLIBRARIES = libburn/libburn.la libisofs/libisofs.la
|
lib_LTLIBRARIES = libburn/libburn.la
|
||||||
|
|
||||||
## ========================================================================= ##
|
## ========================================================================= ##
|
||||||
|
|
||||||
# Build libraries
|
# Build libraries
|
||||||
libburn_libburn_la_LDFLAGS = \
|
libburn_libburn_la_LDFLAGS = \
|
||||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
||||||
|
libburn_libburn_la_LIBADD = $(LIBBURN_ARCH_LIBS) $(THREAD_LIBS)
|
||||||
libburn_libburn_la_SOURCES = \
|
libburn_libburn_la_SOURCES = \
|
||||||
libburn/async.c \
|
libburn/async.c \
|
||||||
libburn/async.h \
|
libburn/async.h \
|
||||||
libburn/back_hacks.h \
|
libburn/back_hacks.h \
|
||||||
|
libburn/cleanup.c \
|
||||||
|
libburn/cleanup.h \
|
||||||
libburn/crc.c \
|
libburn/crc.c \
|
||||||
libburn/crc.h \
|
libburn/crc.h \
|
||||||
libburn/debug.c \
|
libburn/debug.c \
|
||||||
@ -26,14 +29,17 @@ libburn_libburn_la_SOURCES = \
|
|||||||
libburn/lec.c \
|
libburn/lec.c \
|
||||||
libburn/lec.h \
|
libburn/lec.h \
|
||||||
libburn/libburn.h \
|
libburn/libburn.h \
|
||||||
libburn/message.c \
|
libburn/libdax_audioxtr.h \
|
||||||
libburn/message.h \
|
libburn/libdax_audioxtr.c \
|
||||||
|
libburn/libdax_msgs.h \
|
||||||
|
libburn/libdax_msgs.c \
|
||||||
libburn/mmc.c \
|
libburn/mmc.c \
|
||||||
libburn/mmc.h \
|
libburn/mmc.h \
|
||||||
libburn/null.c \
|
libburn/null.c \
|
||||||
libburn/null.h \
|
libburn/null.h \
|
||||||
libburn/options.c \
|
libburn/options.c \
|
||||||
libburn/options.h \
|
libburn/options.h \
|
||||||
|
libburn/os.h \
|
||||||
libburn/read.c \
|
libburn/read.c \
|
||||||
libburn/read.h \
|
libburn/read.h \
|
||||||
libburn/sbc.c \
|
libburn/sbc.c \
|
||||||
@ -57,42 +63,20 @@ libburn_libburn_la_SOURCES = \
|
|||||||
libburn/write.h \
|
libburn/write.h \
|
||||||
version.h
|
version.h
|
||||||
|
|
||||||
libisofs_libisofs_la_LDFLAGS = \
|
## libburn/sg-@ARCH@.c \
|
||||||
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
|
||||||
libisofs_libisofs_la_SOURCES = \
|
|
||||||
libisofs/tree.h \
|
|
||||||
libisofs/tree.c \
|
|
||||||
libisofs/volume.h \
|
|
||||||
libisofs/volume.c \
|
|
||||||
libisofs/util.h \
|
|
||||||
libisofs/util.c \
|
|
||||||
libisofs/ecma119.c \
|
|
||||||
libisofs/ecma119.h \
|
|
||||||
libisofs/ecma119_tree.c \
|
|
||||||
libisofs/ecma119_tree.h \
|
|
||||||
libisofs/susp.h \
|
|
||||||
libisofs/susp.c \
|
|
||||||
libisofs/rockridge.h \
|
|
||||||
libisofs/rockridge.c \
|
|
||||||
libisofs/joliet.c \
|
|
||||||
libisofs/joliet.h \
|
|
||||||
libisofs/exclude.c \
|
|
||||||
libisofs/exclude.h \
|
|
||||||
libisofs/hash.h \
|
|
||||||
libisofs/hash.c
|
|
||||||
|
|
||||||
libinclude_HEADERS = \
|
libinclude_HEADERS = \
|
||||||
libburn/libburn.h \
|
libburn/libburn.h
|
||||||
libisofs/libisofs.h
|
|
||||||
|
|
||||||
## ========================================================================= ##
|
## ========================================================================= ##
|
||||||
|
|
||||||
## Build test applications
|
## Build test applications
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
test/libburner \
|
test/libburner \
|
||||||
test/iso \
|
test/telltoc \
|
||||||
|
test/dewav \
|
||||||
|
test/fake_au \
|
||||||
test/poll \
|
test/poll \
|
||||||
test/toc \
|
|
||||||
test/structest
|
test/structest
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
@ -101,24 +85,27 @@ bin_PROGRAMS = \
|
|||||||
test_libburner_CPPFLAGS = -Ilibburn
|
test_libburner_CPPFLAGS = -Ilibburn
|
||||||
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_libburner_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_libburner_SOURCES = test/libburner.c
|
test_libburner_SOURCES = test/libburner.c
|
||||||
|
test_telltoc_CPPFLAGS = -Ilibburn
|
||||||
|
test_telltoc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
|
test_telltoc_SOURCES = test/telltoc.c
|
||||||
|
test_dewav_CPPFLAGS = -Ilibburn
|
||||||
|
test_dewav_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
|
test_dewav_SOURCES = test/dewav.c
|
||||||
|
test_fake_au_CPPFLAGS =
|
||||||
|
test_fake_au_LDADD =
|
||||||
|
test_fake_au_SOURCES = test/fake_au.c
|
||||||
test_poll_CPPFLAGS = -Ilibburn
|
test_poll_CPPFLAGS = -Ilibburn
|
||||||
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_poll_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_poll_SOURCES = test/poll.c
|
test_poll_SOURCES = test/poll.c
|
||||||
test_toc_CPPFLAGS = -Ilibburn
|
|
||||||
test_toc_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
|
||||||
test_toc_SOURCES = test/toc.c
|
|
||||||
test_structest_CPPFLAGS = -Ilibburn
|
test_structest_CPPFLAGS = -Ilibburn
|
||||||
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
test_structest_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
test_structest_SOURCES = test/structest.c
|
test_structest_SOURCES = test/structest.c
|
||||||
test_iso_CPPFLAGS = -Ilibisofs
|
|
||||||
test_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS)
|
|
||||||
test_iso_SOURCES = test/iso.c
|
|
||||||
|
|
||||||
## cdrskin construction site - ts A60816
|
## cdrskin construction site - ts A60816 - A70210
|
||||||
cdrskin_cdrskin_CPPFLAGS = -Ilibburn
|
cdrskin_cdrskin_CPPFLAGS = -Ilibburn
|
||||||
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_2_2
|
cdrskin_cdrskin_CFLAGS = -DCdrskin_libburn_0_3_2
|
||||||
cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
cdrskin_cdrskin_LDADD = $(libburn_libburn_la_OBJECTS) $(THREAD_LIBS)
|
||||||
cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cleanup.c cdrskin/cleanup.h cdrskin/cdrskin_timestamp.h
|
cdrskin_cdrskin_SOURCES = cdrskin/cdrskin.c cdrskin/cdrfifo.c cdrskin/cdrfifo.h cdrskin/cdrskin_timestamp.h
|
||||||
##
|
##
|
||||||
## Open questions: how to compute $timestamp and express -DX="$timestamp"
|
## Open questions: how to compute $timestamp and express -DX="$timestamp"
|
||||||
##
|
##
|
||||||
@ -158,13 +145,9 @@ uninstall-local:
|
|||||||
|
|
||||||
# Indent source files
|
# Indent source files
|
||||||
indent_files = \
|
indent_files = \
|
||||||
$(libisofs_libisofs_la_SOURCES) \
|
|
||||||
$(libburn_libburn_la_SOURCES) \
|
$(libburn_libburn_la_SOURCES) \
|
||||||
$(test_libburner_SOURCES) \
|
|
||||||
$(test_poll_SOURCES) \
|
$(test_poll_SOURCES) \
|
||||||
$(test_toc_SOURCES) \
|
$(test_structest_SOURCES)
|
||||||
$(test_structest_SOURCES) \
|
|
||||||
$(test_iso_SOURCES)
|
|
||||||
|
|
||||||
|
|
||||||
indent: $(indent_files)
|
indent: $(indent_files)
|
||||||
@ -180,12 +163,13 @@ indent: $(indent_files)
|
|||||||
|
|
||||||
# Extra things
|
# Extra things
|
||||||
nodist_pkgconfig_DATA = \
|
nodist_pkgconfig_DATA = \
|
||||||
libburn-1.pc \
|
libburn-1.pc
|
||||||
libisofs-1.pc
|
|
||||||
|
# http://www.nada.kth.se/cgi-bin/info?(automake.info)Man%20pages
|
||||||
|
man_MANS = cdrskin/cdrskin.1
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
libburn-1.pc.in \
|
libburn-1.pc.in \
|
||||||
libisofs-1.pc.in \
|
|
||||||
version.h.in \
|
version.h.in \
|
||||||
doc/comments \
|
doc/comments \
|
||||||
doc/doxygen.conf.in \
|
doc/doxygen.conf.in \
|
||||||
@ -196,8 +180,19 @@ EXTRA_DIST = \
|
|||||||
cdrskin/README \
|
cdrskin/README \
|
||||||
cdrskin/cdrecord_spy.sh \
|
cdrskin/cdrecord_spy.sh \
|
||||||
cdrskin/compile_cdrskin.sh \
|
cdrskin/compile_cdrskin.sh \
|
||||||
|
cdrskin/convert_man_to_html.sh \
|
||||||
cdrskin/changelog.txt \
|
cdrskin/changelog.txt \
|
||||||
cdrskin/cdrskin_eng.html \
|
cdrskin/cdrskin_eng.html \
|
||||||
cdrskin/wiki_plain.txt \
|
cdrskin/wiki_plain.txt \
|
||||||
COPYING
|
cdrskin/cleanup.h \
|
||||||
|
cdrskin/cleanup.c \
|
||||||
|
libburn/os-freebsd.h \
|
||||||
|
libburn/os-linux.h \
|
||||||
|
libburn/sg-freebsd.c \
|
||||||
|
libburn/sg-linux.c \
|
||||||
|
COPYING \
|
||||||
|
NEWS \
|
||||||
|
ChangeLog \
|
||||||
|
INSTALL \
|
||||||
|
$(man_MANS)
|
||||||
|
|
||||||
|
109
README
109
README
@ -1,63 +1,77 @@
|
|||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
libburn.pykix.org
|
libburnia.pykix.org
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
This all is under GPL.
|
This all is under GPL.
|
||||||
(See GPL reference, our clarification and commitment at the end of this text)
|
(See GPL reference, our clarification and commitment at the end of this text)
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
libburn.pykix.org
|
libburnia.pykix.org
|
||||||
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
|
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
|
||||||
Copyright (C) 2006 Mario Danic, Thomas Schmitt
|
Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt
|
||||||
|
|
||||||
Still containing parts of
|
Still containing parts of
|
||||||
Libburn. By Derek Foreman <derek@signalmarketing.com> and
|
Libburn. By Derek Foreman <derek@signalmarketing.com> and
|
||||||
Ben Jansens <xor@orodu.net>
|
Ben Jansens <xor@orodu.net>
|
||||||
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
||||||
These parts are to be replaced by own code of above libburn.pykix.org-copyright
|
These parts are to be replaced by own code of above libburnia.pykix.org
|
||||||
holders and then libburn.pykix.org is to be their sole copyright.
|
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
|
This is done to achieve the right to issue the clarification and the
|
||||||
commitment as written at the end of this text.
|
commitment as written at the end of this text.
|
||||||
The rights and merits of the Libburn-copyright holders Derek Foreman and
|
The rights and merits of the Libburn-copyright holders Derek Foreman and
|
||||||
Ben Jansens will be duely respected.
|
Ben Jansens will be duely respected.
|
||||||
|
|
||||||
This libburn.pykix.org toplevel README (C) 2006 Thomas Schmitt
|
This libburnia.pykix.org toplevel README (C) 2006-2007 Thomas Schmitt
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
Build and Installation
|
Build and Installation
|
||||||
|
|
||||||
Our build system is based on autotools.
|
Our build system is based on autotools. For preparing the build of a SVN
|
||||||
User experience tells us that you will need at least autotools version 1.7.
|
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
|
||||||
|
|
||||||
To build libburn.pykix.org and its subprojects it should be sufficient to go
|
Alternatively you may unpack a release tarball for which you do not need
|
||||||
into its toplevel directory and execute
|
autotools installed.
|
||||||
./bootstrap (needed if you downloaded from SVN and not a release tarball)
|
|
||||||
|
To build a libburnia.pykix.org subproject it should be sufficient to go
|
||||||
|
into its toplevel directory (here: "libburn_pykix") and execute
|
||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
|
|
||||||
To make the libraries accessible for running resp. developing applications
|
To make the libraries accessible for running resp. developing applications
|
||||||
make install
|
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 libburn.pykix.org
|
|
||||||
|
|
||||||
libburn.pykix.org is an open-source library for reading, mastering and writing
|
Overview of libburnia.pykix.org
|
||||||
optical discs. For now this means only CD-R and CD-RW.
|
|
||||||
|
libburnia.pykix.org is an open-source software project for reading, mastering
|
||||||
|
and writing optical discs.
|
||||||
|
For now this means only CD media and all single layer DVD media except DVD+R.
|
||||||
|
|
||||||
The project comprises of several more or less interdependent parts which
|
The project comprises of several more or less interdependent parts which
|
||||||
together strive to be a usable foundation for application development.
|
together strive to be a usable foundation for application development.
|
||||||
These are libraries, language bindings, and middleware binaries which emulate
|
These are libraries, language bindings, and middleware binaries which emulate
|
||||||
classical (and valuable) Linux tools.
|
classical (and valuable) Linux tools.
|
||||||
|
|
||||||
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
||||||
this for now, because of our history. The project could need advise from or
|
we would need : login on a development machine resp. a live OS on CD or DVD,
|
||||||
membership of skilled kernel people and people who know how to talk CD/DVD
|
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
||||||
drives into doing things.
|
volunteers for testing of realistic use cases.
|
||||||
|
|
||||||
We do have a workable code base for burning data CDs, though. The burn API is
|
We have a workable code base for burning CD and most single layer DVD.
|
||||||
quite comprehensively documented and can be used to build a presentable
|
The burn API is quite comprehensively documented and can be used to build a
|
||||||
application.
|
presentable application.
|
||||||
We do have a functional binary which emulates parts of cdrecord in order to
|
We 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
|
prove that usability, and in order to allow you to explore libburnia's scope
|
||||||
by help of existing cdrecord frontends.
|
by help of existing cdrecord frontends.
|
||||||
|
|
||||||
The project components (list subject to growth, hopefully):
|
The project components (list subject to growth, hopefully):
|
||||||
@ -65,20 +79,25 @@ The project components (list subject to growth, hopefully):
|
|||||||
- libburn is the library by which preformatted data get onto optical media.
|
- 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
|
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
|
||||||
/dev/hdX (e.g. on kernel 2.6).
|
/dev/hdX (e.g. on kernel 2.6).
|
||||||
libburn is the foundation of our cdrecord emulation.
|
libburn is the foundation of our cdrecord emulation. Its code is
|
||||||
|
independent of cdrecord. Its DVD capabilities are learned from
|
||||||
|
studying the code of dvd+rw-tools and MMC-5 specs. No code but only
|
||||||
|
the pure SCSI knowledge has been taken from dvd+rw-tools, though.
|
||||||
|
|
||||||
- libisofs is the library to pack up hard disk files and directories into a
|
- 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.
|
ISO 9660 disk image. This may then be brought to media via libburn.
|
||||||
libisofs is to be the foundation of our upcoming mkisofs emulation.
|
libisofs is to be the foundation of our upcoming mkisofs emulation.
|
||||||
|
|
||||||
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
|
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
|
||||||
Cdrecord is a powerful GPL'ed burn program included in Joerg
|
Cdrecord is a powerful GPL'ed burn program included in Joerg
|
||||||
Schilling's cdrtools. cdrskin strives to be a second source for
|
Schilling's cdrtools. cdrskin strives to be a second source for
|
||||||
the services traditionally provided by cdrecord.
|
the services traditionally provided by cdrecord. Additionally it
|
||||||
|
provides libburn's DVD capabilities, where only -sao is compatible
|
||||||
|
with cdrecord.
|
||||||
cdrskin does not contain any bytes copied from cdrecord's sources.
|
cdrskin does not contain any bytes copied from cdrecord's sources.
|
||||||
Many bytes have been copied from the message output of cdrecord
|
Many bytes have been copied from the message output of cdrecord
|
||||||
runs, though.
|
runs, though.
|
||||||
See cdrskin/README for more.
|
See cdrskin/README and man cdrskin/cdrskin.1 for more.
|
||||||
|
|
||||||
- test is a collection of application gestures and examples given by the
|
- test is a collection of application gestures and examples given by the
|
||||||
authors of the library features. The main API example for libburn
|
authors of the library features. The main API example for libburn
|
||||||
@ -146,6 +165,36 @@ Project history as far as known to me:
|
|||||||
|
|
||||||
- 16th September 2006 feature freeze for release of libburn-0.2.2 .
|
- 16th September 2006 feature freeze for release of libburn-0.2.2 .
|
||||||
|
|
||||||
|
- 20th September 2006 release of libburn-0.2.2 .
|
||||||
|
|
||||||
|
- 26th October 2006 feature freeze for cdrskin-0.2.4 based on libburn-0.2.3 .
|
||||||
|
This version of cdrskin is much more cdrecord compatible in repect
|
||||||
|
to drive addressing and audio features.
|
||||||
|
|
||||||
|
- 30th October 2006 release of cdrskin-0.2.4 .
|
||||||
|
|
||||||
|
- 13th November 2006 splitting releases of libburn+cdrskin from libisofs.
|
||||||
|
|
||||||
|
- 24th November 2006 release of libburn-0.2.6 and cdrskin-0.2.6 . cdrskin has
|
||||||
|
become suitable for unaware frontends as long as they perform only the core
|
||||||
|
of cdrecord use cases (including open-ended input streams, audio, and
|
||||||
|
multi-session).
|
||||||
|
|
||||||
|
- 28th November 2006 the umbrella project which encloses both, libisofs and
|
||||||
|
libburn, is now called libburnia. For the origin of this name, see
|
||||||
|
http://en.wikipedia.org/wiki/Liburnians .
|
||||||
|
|
||||||
|
- 16th January 2007 release of libburn-0.3.0 and cdrskin-0.3.0 . Now the scope
|
||||||
|
is widened to a first class of DVD media: overwriteable single layer types
|
||||||
|
DVD-RAM, DVD+RW, DVD-RW. This is not a cdrecord emulation but rather inspired
|
||||||
|
by dvd+rw-tools' "poor man" writing facility for this class of media.
|
||||||
|
Taking a bow towards Andy Polyakov.
|
||||||
|
|
||||||
|
- 11th February 2007 version 0.3.2 covers sequential DVD-RW and DVD-R with
|
||||||
|
multi-session and with DAO.
|
||||||
|
This means all single layer DVD media except DVD+R are supported now.
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
@ -164,9 +213,9 @@ Project history as far as known to me:
|
|||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
Clarification in my name and in the name of Mario Danic, upcoming copyright
|
Clarification in my name and in the name of Mario Danic, upcoming copyright
|
||||||
holders on toplevel of libburn. To be fully in effect after the remaining other
|
holders on toplevel of libburnia. To be fully in effect after the remaining
|
||||||
copyrighted code has been replaced by ours and by copyright-free contributions
|
other copyrighted code has been replaced by ours and by copyright-free
|
||||||
of our friends:
|
contributions of our friends:
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
We, the copyright holders, agree on the interpretation that
|
We, the copyright holders, agree on the interpretation that
|
||||||
|
25
acinclude.m4
25
acinclude.m4
@ -1,18 +1,3 @@
|
|||||||
dnl Function to link an architecture specific file
|
|
||||||
dnl LINK_ARCH_SRC(source_dir, arch, source_file)
|
|
||||||
AC_DEFUN([COPY_ARCH_SRC],
|
|
||||||
[
|
|
||||||
echo "copying $1/$2/$3 -> $1/$3"
|
|
||||||
old="$srcdir/$1/$2/$3"
|
|
||||||
new="$srcdir/$1/$3"
|
|
||||||
cat >$new <<__EOF__
|
|
||||||
/* WARNING: This file was automatically generated!
|
|
||||||
* Original: $old
|
|
||||||
*/
|
|
||||||
__EOF__
|
|
||||||
cat >>$new <$old
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_DEFUN([TARGET_SHIZZLE],
|
AC_DEFUN([TARGET_SHIZZLE],
|
||||||
[
|
[
|
||||||
ARCH=""
|
ARCH=""
|
||||||
@ -22,6 +7,11 @@ AC_DEFUN([TARGET_SHIZZLE],
|
|||||||
case $target in
|
case $target in
|
||||||
*-*-linux*)
|
*-*-linux*)
|
||||||
ARCH=linux
|
ARCH=linux
|
||||||
|
LIBBURN_ARCH_LIBS=
|
||||||
|
;;
|
||||||
|
*-*-freebsd*)
|
||||||
|
ARCH=freebsd
|
||||||
|
LIBBURN_ARCH_LIBS=-lcam
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
AC_ERROR([You are attempting to compile for an unsupported platform])
|
AC_ERROR([You are attempting to compile for an unsupported platform])
|
||||||
@ -29,9 +19,4 @@ AC_DEFUN([TARGET_SHIZZLE],
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
AC_MSG_RESULT([$ARCH])
|
AC_MSG_RESULT([$ARCH])
|
||||||
|
|
||||||
# this doesn't actually do anything yet.. but it will someday when we port
|
|
||||||
# libburn
|
|
||||||
|
|
||||||
#COPY_ARCH_SRC(libburn, $ARCH, transport.c)
|
|
||||||
])
|
])
|
||||||
|
@ -3,4 +3,8 @@
|
|||||||
aclocal
|
aclocal
|
||||||
libtoolize --copy --force
|
libtoolize --copy --force
|
||||||
autoconf
|
autoconf
|
||||||
|
|
||||||
|
# ts A61101 : libburn is not prepared for config.h
|
||||||
|
# autoheader
|
||||||
|
|
||||||
automake --foreign --add-missing --copy --include-deps
|
automake --foreign --add-missing --copy --include-deps
|
||||||
|
314
cdrskin/README
314
cdrskin/README
@ -1,5 +1,5 @@
|
|||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
libburn.pykix.org scdbackup.sourceforge.net/cdrskin_eng.html
|
libburnia.pykix.org scdbackup.sourceforge.net/cdrskin_eng.html
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
Installation instructions at about line 60. First the legal stuff:
|
Installation instructions at about line 60. First the legal stuff:
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
@ -7,31 +7,31 @@ This all is under GPL.
|
|||||||
(See GPL reference, our clarification and commitment at the end of this text)
|
(See GPL reference, our clarification and commitment at the end of this text)
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
Based on and sub project of:
|
Based on and sub project of:
|
||||||
libburn.pykix.org
|
libburnia.pykix.org
|
||||||
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
|
By Mario Danic <mario.danic@gmail.com> and Thomas Schmitt <scdbackup@gmx.net>
|
||||||
Copyright (C) 2006 Mario Danic, Thomas Schmitt
|
Copyright (C) 2006-2007 Mario Danic, Thomas Schmitt
|
||||||
|
|
||||||
libburn.pykix.org is inspired by and in other components still containing
|
libburnia.pykix.org is inspired by and in other components still containing
|
||||||
parts of
|
parts of
|
||||||
Libburn. By Derek Foreman <derek@signalmarketing.com> and
|
Libburn. By Derek Foreman <derek@signalmarketing.com> and
|
||||||
Ben Jansens <xor@orodu.net>
|
Ben Jansens <xor@orodu.net>
|
||||||
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
Copyright (C) 2002-2006 Derek Foreman and Ben Jansens
|
||||||
See toplevel README for an overview of the current copyright situation in
|
See toplevel README for an overview of the current copyright situation in
|
||||||
libburn.pykix.org.
|
libburnia.pykix.org.
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
My thanks to the above authors (except myself, of course) for making the
|
My thanks to the above authors (except myself, of course) for making the
|
||||||
following possible.
|
following possible.
|
||||||
|
|
||||||
cdrskin. By Thomas Schmitt <scdbackup@gmx.net>
|
cdrskin. By Thomas Schmitt <scdbackup@gmx.net>
|
||||||
Integrated sub project of libburn.pykix.org but also published via:
|
Integrated sub project of libburnia.pykix.org but also published via:
|
||||||
http://scdbackup.sourceforge.net/cdrskin_eng.html
|
http://scdbackup.sourceforge.net/cdrskin_eng.html
|
||||||
http://scdbackup.sourceforge.net/cdrskin-0.2.2.tar.gz
|
http://scdbackup.sourceforge.net/cdrskin-0.3.2.pl00.tar.gz
|
||||||
Copyright (C) 2006 Thomas Schmitt
|
Copyright (C) 2006-2007 Thomas Schmitt
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
On top of libburn there is implemented cdrskin 0.2.2, a limited cdrecord
|
On top of libburn there is implemented cdrskin 0.3.2, a limited cdrecord
|
||||||
compatibility wrapper which allows to use some libburn features from
|
compatibility wrapper which allows to use some libburn features from
|
||||||
the command line.
|
the command line.
|
||||||
Interested users of cdrecord are invited to participate in the development
|
Interested users of cdrecord are invited to participate in the development
|
||||||
@ -46,9 +46,10 @@ protection against possible damages. You use this on your own risk.
|
|||||||
Don't blame me or other authors of libburn if anything goes wrong.
|
Don't blame me or other authors of libburn if anything goes wrong.
|
||||||
|
|
||||||
I used it on my own risk with :
|
I used it on my own risk with :
|
||||||
SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner
|
SuSE 7.2, kernel 2.4.4, ide-scsi emulation, LITE-ON LTR48125S CD burner, 2002
|
||||||
SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner
|
SuSE 9.0, kernel 2.4.21, ide-scsi emulation, LG GSA-4082B CD/DVD burner, 2004
|
||||||
RIP-14.4, kernel 2.6.14, no ide-scsi, with both above burners
|
NEC ND-4570A CD/DVD burner, 2006
|
||||||
|
RIP-14.4, kernel 2.6.14, no ide-scsi, with all above burners
|
||||||
|
|
||||||
It fails to compile or run on SuSE 6.4 (kernel 2.2.14).
|
It fails to compile or run on SuSE 6.4 (kernel 2.2.14).
|
||||||
It does not find the IDE CD burner on SuSE 7.2 without ide-scsi.
|
It does not find the IDE CD burner on SuSE 7.2 without ide-scsi.
|
||||||
@ -58,16 +59,16 @@ systems, including 64 bit systems. (Further reports are welcome.)
|
|||||||
|
|
||||||
Compilation, First Glimpse, Installation
|
Compilation, First Glimpse, Installation
|
||||||
|
|
||||||
Obtain cdrskin-0.2.2.tar.gz , take it to a directory of your choice and do:
|
Obtain cdrskin-0.3.2.pl00.tar.gz, take it to a directory of your choice and do:
|
||||||
|
|
||||||
tar xzf cdrskin-0.2.2.tar.gz
|
tar xzf cdrskin-0.3.2.pl00.tar.gz
|
||||||
cd cdrskin-0.2.2
|
cd cdrskin-0.3.2
|
||||||
|
|
||||||
Or obtain a libburn.pykix.org SVN snapshot,
|
Or obtain a libburnia.pykix.org SVN snapshot,
|
||||||
go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
|
go into the toplevel directory of the snapshot (e.g. cd libburn_pykix ),
|
||||||
and execute the autotools script ./bootstrap . Use autools version >= 1.7 .
|
and execute the autotools script ./bootstrap . Use autools version >= 1.7 .
|
||||||
|
|
||||||
Within that toplevel directory of either cdrskin-0.2.2 or libburn then execute:
|
Within that toplevel directory of either cdrskin-0.3.2 or libburn then execute:
|
||||||
|
|
||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
@ -85,10 +86,11 @@ In order to surely get a standalone binary, execute
|
|||||||
|
|
||||||
cdrskin/compile_cdrskin.sh
|
cdrskin/compile_cdrskin.sh
|
||||||
|
|
||||||
Version identification an help texts available afterwards:
|
Version identification and help texts available afterwards:
|
||||||
cdrskin/cdrskin -version
|
cdrskin/cdrskin -version
|
||||||
cdrskin/cdrskin --help
|
cdrskin/cdrskin --help
|
||||||
cdrskin/cdrskin -help
|
cdrskin/cdrskin -help
|
||||||
|
man cdrskin/cdrskin.1
|
||||||
|
|
||||||
Install (eventually as superuser) cdrskin to a directory where it can be found:
|
Install (eventually as superuser) cdrskin to a directory where it can be found:
|
||||||
If cdrskin was already installed by a previous version, or by "make install"
|
If cdrskin was already installed by a previous version, or by "make install"
|
||||||
@ -107,6 +109,10 @@ It will not collide with an installed version of libburn either.
|
|||||||
But libpthread must be installed on the system and glibc has to match. (See
|
But libpthread must be installed on the system and glibc has to match. (See
|
||||||
below for a way to create a statically linked binary.)
|
below for a way to create a statically linked binary.)
|
||||||
|
|
||||||
|
To install the man page, you may do: echo $MANPATH and choose one of the
|
||||||
|
listed directories to copy the man-page under its ./man1 directory. Like:
|
||||||
|
cp cdrskin/cdrskin.1 /usr/share/man/man1/cdrskin.1
|
||||||
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
@ -124,62 +130,83 @@ On Linux 2.4 this would be some /dev/sgN and on 2.6. some /dev/hdX.
|
|||||||
|
|
||||||
The output of cdrskin --devices might look like
|
The output of cdrskin --devices might look like
|
||||||
|
|
||||||
0 dev='/dev/sg0' rwrwr- : 'TEAC' 'CD-ROM CD-532S'
|
0 dev='/dev/sg0' rwrwr- : '_NEC' 'DVD_RW ND-4570A'
|
||||||
1 dev='/dev/sg1' rwrw-- : 'LITE-ON' 'LTR-48125S'
|
1 dev='/dev/sg1' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
||||||
|
|
||||||
So full and insecure enabling of both for everybody would look like
|
So full and insecure enabling of both for everybody would look like
|
||||||
|
|
||||||
chmod a+rw /dev/sg0 /dev/sg1
|
chmod a+rw /dev/sg0 /dev/sg1
|
||||||
|
|
||||||
(The CD-ROM is in these examples only for demonstrating the presence of another
|
|
||||||
SCSI device. This /dev/sg0 may be left as it is and stay invisible for normal
|
|
||||||
users.)
|
|
||||||
|
|
||||||
I strongly discourage to run cdrskin with setuid root or via sudo !
|
I strongly discourage to run cdrskin with setuid root or via sudo !
|
||||||
It is not checked for the necessary degree of hacker safety.
|
It is not checked for the necessary degree of hacker safety.
|
||||||
|
|
||||||
|
|
||||||
Usage examples
|
Usage examples
|
||||||
|
|
||||||
|
For options and recordable media classes see
|
||||||
|
man 1 cdrskin
|
||||||
|
|
||||||
Get an overview of cdrecord style addresses of available devices
|
Get an overview of cdrecord style addresses of available devices
|
||||||
cdrskin -scanbus
|
cdrskin -scanbus
|
||||||
|
cdrskin dev=ATA -scanbus
|
||||||
|
cdrskin --devices
|
||||||
|
|
||||||
|
Adresses reported with dev=ATA need prefix "ATA:". Address examples:
|
||||||
|
dev=0,1,0 dev=ATA:1,0,0 dev=/dev/sg1 dev=/dev/hdc
|
||||||
|
See also "Drive Addressing" below.
|
||||||
|
|
||||||
Obtain some info about the drive
|
Obtain some info about the drive
|
||||||
cdrskin dev=1,1,0 -checkdrive
|
cdrskin dev=0,1,0 -checkdrive
|
||||||
|
|
||||||
Obtain some info about the drive and the inserted media
|
Obtain some info about the drive and the inserted media
|
||||||
cdrskin dev=1,1,0 -atip
|
cdrskin dev=0,1,0 -atip -v
|
||||||
|
|
||||||
Thoroughly blank a CD-RW
|
Make used CD-RW or used unformatted DVD-RW writeable again
|
||||||
cdrskin -v dev=1,1,0 blank=all -eject
|
cdrskin -v dev=0,1,0 blank=fast -eject
|
||||||
|
|
||||||
Blank CD-RW sufficiently for making it ready for overwrite
|
Format DVD-RW to avoid need for blanking before re-use
|
||||||
cdrskin -v dev=1,1,0 blank=fast -eject
|
cdrskin -v dev=0,1,0 blank=format_overwrite
|
||||||
|
|
||||||
Burn image file my_image.iso to CD
|
De-format DVD-RW to make it capable of multi-session again
|
||||||
cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree padsize=300k \
|
cdrskin -v dev=/dev/sr0 blank=deformat_sequential
|
||||||
|
|
||||||
|
Burn image file my_image.iso to media
|
||||||
|
cdrskin -v dev=0,1,0 speed=12 fs=8m driveropts=burnfree padsize=300k \
|
||||||
-eject my_image.iso
|
-eject my_image.iso
|
||||||
|
|
||||||
Burn a compressed afio archive to CD on-the-fly
|
Write several sessions to the same CD or DVD-R[W]
|
||||||
find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao \
|
cdrskin dev=/dev/hdc padsize=300k -multi 1.iso
|
||||||
driveropts=burnfree padsize=300k tsize=650m -
|
cdrskin dev=/dev/hdc padsize=300k -multi -tao 2.iso
|
||||||
|
cdrskin dev=/dev/hdc padsize=300k -multi -tao 3.iso
|
||||||
|
cdrskin dev=/dev/hdc padsize=300k -tao 4.iso
|
||||||
|
|
||||||
Burn 5 audio tracks to CD
|
Get CD or DVD-R[W] multi-session info for option -C of program mkisofs:
|
||||||
|
c_values=$(cdrskin dev=/dev/hdc -msinfo 2>/dev/null)
|
||||||
|
mkisofs ... -C "$c_values" ...
|
||||||
|
|
||||||
|
Burn a compressed afio archive to media on-the-fly
|
||||||
|
find . | afio -oZ - | cdrskin -v dev=0,1,0 fs=32m speed=8 -tao \
|
||||||
|
driveropts=burnfree padsize=300k -
|
||||||
|
|
||||||
|
Burn 6 audio tracks from files with different formats to CD (not to any DVD).
|
||||||
|
Anything except .wav or .au files has to be converted into raw format first.
|
||||||
|
See below "Audio CD" for specifications.
|
||||||
ogg123 -d raw -f track01.cd /path/to/track1.ogg
|
ogg123 -d raw -f track01.cd /path/to/track1.ogg
|
||||||
oggdec -R -o track02.cd /path/to/track2.ogg
|
oggdec -R -o track02.cd /path/to/track2.ogg
|
||||||
lame --decode -t /path/to/track3.mp3 track03.cd
|
lame --decode -t /path/to/track3.mp3 track03.cd
|
||||||
madplay -o raw:track04.cd /path/to/track4.mp3
|
madplay -o raw:track04.cd /path/to/track4.mp3
|
||||||
mppdec --raw-le /path/to/track5.mpc track05.cd
|
mppdec --raw-le /path/to/track5.mpc track05.cd
|
||||||
cdrskin dev=/dev/hdc blank=fast fs=0 -eject -audio track0[1-5].cd
|
|
||||||
|
|
||||||
|
cdrskin -v dev=0,1,0 blank=fast -eject speed=48 -sao \
|
||||||
|
-audio -swab track0[1-5].cd /path/to/track6.wav
|
||||||
|
|
||||||
|
|
||||||
Usage example with http://scdbackup.sourceforge.net
|
Usage example with http://scdbackup.sourceforge.net
|
||||||
|
|
||||||
Address may be a cdrecord-style "scsibus,target,lun" as listed with
|
Address may be a cdrecord-style "scsibus,target,lun" as listed with
|
||||||
cdrskin -scanbus (but not as listed with cdrecord -scanbus) :
|
cdrskin -scanbus (and hopefully as listed with cdrecord -scanbus) :
|
||||||
|
|
||||||
export SCDBACKUP_SCSI_ADR="1,1,0"
|
export SCDBACKUP_SCSI_ADR="0,1,0"
|
||||||
|
|
||||||
or a device file address as listed by --devices with an accessible drive :
|
or a device file address as listed by --devices with an accessible drive :
|
||||||
|
|
||||||
@ -187,7 +214,7 @@ or a device file address as listed by --devices with an accessible drive :
|
|||||||
|
|
||||||
Set usage of cdrskin with appropriate options rather than cdrecord :
|
Set usage of cdrskin with appropriate options rather than cdrecord :
|
||||||
|
|
||||||
export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m"
|
export SCDBACKUP_CDRECORD="cdrskin -v -v"
|
||||||
|
|
||||||
Run a backup :
|
Run a backup :
|
||||||
|
|
||||||
@ -196,17 +223,14 @@ Run a backup :
|
|||||||
|
|
||||||
Restrictions
|
Restrictions
|
||||||
|
|
||||||
The convenient burn mode TAO is not available with libburn yet.
|
The major restrictions are lifted now: audio, TAO, multi-session do work.
|
||||||
Therefore it has to be defaulted to mode SAO which needs to know the track
|
|
||||||
size in advance. non-cdrecord option tao_to_sao_tsize=650m causes each CD
|
|
||||||
to get burned up to 650 MB regardless of the payload size.
|
|
||||||
|
|
||||||
Audio features are incomplete in respect to cdrecord. Well prepaired track
|
Many cdrecord options are still unsupported, though.
|
||||||
files should get burned flawlessly, thanks to Lorenzo Taylor.
|
If you have use cases for them, please report your wishes and expectations.
|
||||||
Builtin extraction of raw audio data from filetypes .au and .wav is not
|
|
||||||
implemented yet. See chapter "Audio CD" for details.
|
DVD support is restricted to single layer overwriteable DVD (-RAM, +RW, -RW)
|
||||||
|
for now.
|
||||||
|
|
||||||
No multi session yet ... Please report your wishes.
|
|
||||||
|
|
||||||
|
|
||||||
Inspiration and Standard
|
Inspiration and Standard
|
||||||
@ -225,13 +249,50 @@ Actually i, Thomas Schmitt, am a devoted user of cdrecord via my project
|
|||||||
scdbackup which still runs a bit better with cdrecord than with cdrskin. TAO.
|
scdbackup which still runs a bit better with cdrecord than with cdrskin. TAO.
|
||||||
I have the hope that Joerg feels more flattered than annoyed by cdrskin.
|
I have the hope that Joerg feels more flattered than annoyed by cdrskin.
|
||||||
|
|
||||||
|
Many thanks to Andy Polyakov for his dvd+rw-tools
|
||||||
|
http://fy.chalmers.se/~appro/linux/DVD+RW/tools
|
||||||
|
which provide me with examples and pointers into MMC specs for DVD writing.
|
||||||
|
|
||||||
|
|
||||||
|
Drive Addressing
|
||||||
|
|
||||||
|
Drives get addressed either via their cdrecord-style addresses as listed
|
||||||
|
with option -scanbus (see below "Pseudo-SCSI Adresses") or via the paths
|
||||||
|
of device files.
|
||||||
|
Not only device files listed by --devices may be used but also device files
|
||||||
|
which via their major,minor numbers point to the same device driver as
|
||||||
|
a listed device file.
|
||||||
|
|
||||||
|
Helpful with Linux kernel 2.4 is a special SCSI feature:
|
||||||
|
It is possible to address a scsi(-emulated) drive via associated device files
|
||||||
|
which are not listed by option --devices but point to the same SCSI addresses
|
||||||
|
as listed device files. This addressing via e.g. /dev/sr0 or /dev/scd1 is
|
||||||
|
compatible with generic read programs like dd and with write program growisofs.
|
||||||
|
|
||||||
Pseudo-SCSI Adresses
|
Pseudo-SCSI Adresses
|
||||||
|
|
||||||
cdrecord and cdrskin share only some syntax of addresses but not the meaning
|
cdrecord and cdrskin share the syntax of SCSI addresses but not necessarily
|
||||||
of the components. A cdrecord-style address for cdrskin
|
the meaning of the components. A cdrecord-style address for cdrskin
|
||||||
[[prefix:]scsibus,]target,lun
|
[prefix:]scsibus,target,lun
|
||||||
corresponds either to a device file address or to a libburn drive number.
|
can be interpreted in two different modes.
|
||||||
|
|
||||||
|
Standard mode tries to be compatible to original cdrecord. This should be true
|
||||||
|
with (emulated) SCSI where the /dev/sgN with is looked up with matching
|
||||||
|
scsibus,target,lun as given by the operating system.
|
||||||
|
With dev=ATA: or dev=ATAPI: the translation to /dev/hdX is purely literal
|
||||||
|
but matches the cdrecord addresses on all systems tested so far:
|
||||||
|
X = 'a' + 2 * scsibus + target
|
||||||
|
where target only may have the values 0 or 1.
|
||||||
|
|
||||||
|
In this mode, option -scanbus will list only SCSI devices unless option
|
||||||
|
dev=ATA or dev=ATAPI are given, which will suppress SCSI devices and only
|
||||||
|
show IDE drives (i.e. /dev/hdX without ide-scsi emulation).
|
||||||
|
|
||||||
|
|
||||||
|
In mode --old_pseudo_scsi_adr there is a scsibus,target,lun representation
|
||||||
|
which has nothing to do with SCSI and thus is not compatible to cdrecord.
|
||||||
|
Each number triple corresponds either to a device file address or to a
|
||||||
|
libburn drive number.
|
||||||
Component "scsibus" indicates the translation method. Defined busses are:
|
Component "scsibus" indicates the translation method. Defined busses are:
|
||||||
0 target is the libburn drivenumber as listed with --devices
|
0 target is the libburn drivenumber as listed with --devices
|
||||||
1 associated to device file /dev/sgN , target chooses N
|
1 associated to device file /dev/sgN , target chooses N
|
||||||
@ -246,17 +307,24 @@ drive accessability.
|
|||||||
Further busses may emerge as libburn evolves. "prefix" and "lun" may get
|
Further busses may emerge as libburn evolves. "prefix" and "lun" may get
|
||||||
a meaning. To stay upward compatible, use addresses as printed by -scanbus.
|
a meaning. To stay upward compatible, use addresses as printed by -scanbus.
|
||||||
|
|
||||||
|
User Defined Device Address Translation
|
||||||
|
|
||||||
Some programs or users have their own ideas about the address of their burner.
|
Some programs or users have their own ideas about the address of their burner.
|
||||||
K3b 0.10 for example derives cdrecord addresses by own examination of the
|
K3b 0.10 for example derives cdrecord addresses by own examination of the
|
||||||
devices and not by calling cdrecord -scanbus.
|
devices and not by calling cdrecord -scanbus.
|
||||||
To direct such callers to the appropriate drives, cdrskin allows to define
|
Standard mode will hopefully be fully compatible with their ideas.
|
||||||
device address aliases. Like
|
|
||||||
|
Old frontends which do not know dev=ATA or dev=ATAPI and which do ask their
|
||||||
|
"cdrecord" via -scanbus may be well served with option --old_pseudo_scsi_adr .
|
||||||
|
|
||||||
|
To direct any remaining stubborn callers to the appropriate drives, cdrskin
|
||||||
|
allows to define device address aliases. Like
|
||||||
cdrskin dev_translation=+1,0,0+/dev/sg1 \
|
cdrskin dev_translation=+1,0,0+/dev/sg1 \
|
||||||
dev_translation=+ATA:1,0,0+/dev/sg1 \
|
dev_translation=+ATA:1,0,0+/dev/sg1 \
|
||||||
dev_translation=-"cd+dvd"-1,1,0 \
|
dev_translation=-"cd+dvd"-0,1,0 \
|
||||||
...
|
...
|
||||||
Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to
|
Any of the addresses dev=1,0,0, dev=ATA:1,0,0, dev=cd+dvd will be mapped to
|
||||||
/dev/sg1 resp. to its standard alias 1,1,0.
|
/dev/sg1 resp. to 0,1,0.
|
||||||
The first character after "dev_translation=" defines the character which
|
The first character after "dev_translation=" defines the character which
|
||||||
separates the two parts of the translation pair. (Above: "+" and "-".)
|
separates the two parts of the translation pair. (Above: "+" and "-".)
|
||||||
|
|
||||||
@ -278,39 +346,135 @@ will be the right one to replace "1,0,0" in above example.
|
|||||||
|
|
||||||
If not --no_rc is the first argument then cdrskin attempts on startup to read
|
If not --no_rc is the first argument then cdrskin attempts on startup to read
|
||||||
arguments from the following three files:
|
arguments from the following three files:
|
||||||
/etc/defaults/cdrskin
|
/etc/default/cdrskin
|
||||||
/etc/opt/cdrskin/rc
|
/etc/opt/cdrskin/rc
|
||||||
|
/etc/cdrskin/cdrskin.conf
|
||||||
$HOME/.cdrskinrc
|
$HOME/.cdrskinrc
|
||||||
The files are read in the sequence given above.
|
The files are read in the sequence given above.
|
||||||
Each readable line is treated as one single argument. No extra blanks,
|
Each readable line is treated as one single argument. No extra blanks.
|
||||||
no comments, no empty lines are permitted.
|
A first character '#' marks a comment, empty lines are ignored.
|
||||||
|
|
||||||
Example content of a startup file:
|
Example content of a startup file:
|
||||||
dev=1,1,0
|
# This is the default device
|
||||||
dev_translation=+1,0,0+1,1,0
|
dev=0,1,0
|
||||||
--fifo_start_empty
|
# To accomodate to eventual remnant cdrskin-0.2.2 addresses
|
||||||
|
dev_translation=+1,0,0+0,1,0
|
||||||
|
|
||||||
|
# Some more options
|
||||||
|
fifo_start_at=0
|
||||||
fs=16m
|
fs=16m
|
||||||
|
|
||||||
|
|
||||||
Audio CD
|
Audio CD
|
||||||
|
|
||||||
Builtin extraction of raw audio data from filetypes .au and .wav is not
|
Lorenzo Taylor enabled option -audio in cdrskin (thanks !) and reports neat
|
||||||
implemented yet. Lorenzo Taylor enabled option -audio in cdrskin (thanks !)
|
results with audio data files which are :
|
||||||
and reports neat results with audio data files which are :
|
headerless PCM (i.e. uncompressed)
|
||||||
headerless
|
44100 Hz sampling rate
|
||||||
44100Hz
|
16 bits per sample
|
||||||
16bit, stereo (or 4-channel if the 4-channel bit is set),
|
stereo (2 channels)
|
||||||
little-endian byte order
|
little-endian byte order with option -swab, or big-endian without -swab
|
||||||
He proposes to extract them from usual audio formats by commands like
|
|
||||||
given above under "Usage examples".
|
Files with name extension .wav get examined wether they are in Microsoft WAVE
|
||||||
|
format with above parameters and eventually get extracted by cdrskin itself.
|
||||||
|
In the same way files with name extension .au get examined wether they are
|
||||||
|
in SUN's audio format. For both formats, track format -audio and eventual
|
||||||
|
endianness option -swab are enabled automatically.
|
||||||
|
|
||||||
|
Any other formats are to be converted to format .wav with above parameters
|
||||||
|
or to be extracted as raw CD track data by commands like those given above
|
||||||
|
under "Usage examples". Those raw files need option -audio and in most cases
|
||||||
|
option -swab to mark them as little-endian/Intel/LSB-first 16-bit data.
|
||||||
|
Incorrect endianness setting results in random noise on CD.
|
||||||
|
|
||||||
The existence of cdrecord-builtin .wav extraction seems to have
|
|
||||||
hampered the development of a standalone stripping tool. If you know
|
|
||||||
a command line that would do the trick, contact me or libburn.pykix.org .
|
|
||||||
I myself am not into audio. So libburn-hackers@pykix.org might be the
|
I myself am not into audio. So libburn-hackers@pykix.org might be the
|
||||||
best address for suggestions, requests and bug reports.
|
best address for suggestions, requests and bug reports.
|
||||||
|
|
||||||
|
|
||||||
|
DVD+RW and DVD-RAM
|
||||||
|
|
||||||
|
DVD+RW and DVD-RAM media get treated as blank media regardless wether they
|
||||||
|
hold data or not. Options -audio and -multi are not allowed. Only one track
|
||||||
|
is allowed. -toc does not return information about the media content.
|
||||||
|
Speed is counted in DVD units (i.e. 1x = 1,385,000 bytes/second). Currently
|
||||||
|
there is no difference between -sao and -tao. If ever, then -tao will be the
|
||||||
|
mode which preserves the current behavior.
|
||||||
|
|
||||||
|
For these media, -msinfo alone would not be enough to perform appending of an
|
||||||
|
ISO filesystem. The filesystem driver will need a hint to find the start of the
|
||||||
|
most recent session. For example put an ISO filesystem at address 1 GB:
|
||||||
|
mkisofs -C 0,524288 ... | \
|
||||||
|
cdrskin dev=/dev/sr0 -v fs=32m -eject speed=4 write_start_address=524288s -
|
||||||
|
The superuser may then do:
|
||||||
|
mount -t iso9660 -o ro,sbsector=524288 /dev/sr0 /mnt
|
||||||
|
Note: On my linux-2.4.21-215 mount works only with sbsector <= 337920 (660 MB).
|
||||||
|
To extend a filesystem already existing at address 0
|
||||||
|
mkisofs -C 0,524288 -M /dev/sr0 ... | cdrskin dev=/dev/sr0 ...
|
||||||
|
Record the number 524288 for usage as first number with -C at the next
|
||||||
|
extension:
|
||||||
|
mkisofs -C 524288,1000000 ... | cdrskin write_start_address=1000000s ...
|
||||||
|
|
||||||
|
Program growisofs can append to an ISO filesystem on DVD+RW by additionally
|
||||||
|
manipulating the first session. cdrskin does not want to get involved so deep
|
||||||
|
into the format of the burned data. Be advised to use growisofs for the
|
||||||
|
task of maintaining extendable ISO-Filesystems on DVD+RW.
|
||||||
|
|
||||||
|
|
||||||
|
DVD-RW and DVD-R
|
||||||
|
|
||||||
|
DVD-RW are usable if formatted to state "Restricted Overwrite" or if in state
|
||||||
|
"Sequential Recording". DVD-R are always in sequential state.
|
||||||
|
|
||||||
|
"Sequential" is the state of unused media and of media previously blanked
|
||||||
|
or written by cdrecord. dvd+rw-format -blank can also achieve this state.
|
||||||
|
The according cdrskin option is blank=deformat_sequential .
|
||||||
|
If "Incremental Streaming" is available, then sequential media are capable
|
||||||
|
of multi-session like CD-R[W]. (But not capable of -audio recording.)
|
||||||
|
This means they need option -multi to stay appendable, need to be blanked
|
||||||
|
to be writeable from start, return useable info with -toc and -msinfo,
|
||||||
|
eventually perform appending automatically.
|
||||||
|
Without "Incremental Streaming" offered by the drive, only write mode DAO is
|
||||||
|
available with sequential DVD-R[W]. It only works with blank media, allows only
|
||||||
|
one single track, no -multi, and demands a fixely predicted track size.
|
||||||
|
(growisofs uses it with DVD-R[W] if option -dvd-compat is given.)
|
||||||
|
|
||||||
|
Overwriteable DVD-RW behave much like DVD+RW. "Restricted" refers only to the
|
||||||
|
granularity of random access and block size which have always to be aligned to
|
||||||
|
full 32 kB. Sequential DVD-RW are converted into overwriteable DVD-RW by
|
||||||
|
cdrskin dev=... -v blank=format_overwrite
|
||||||
|
(Command dvd+rw-format -force can achieve "Restricted Overwrite", too.)
|
||||||
|
|
||||||
|
Formatting or first use of freshly formatted DVD-RW can produce unusual noises
|
||||||
|
from the drive and last several minutes. Depending on mutual compatibility of
|
||||||
|
drive and media, formatting can yield unusable media. It seems that those die
|
||||||
|
too on blanking by cdrecord, dvd+rw-format or cdrskin. Perils of DVD-RW.
|
||||||
|
|
||||||
|
There are three DVD-RW formatting variants with cdrskin currently:
|
||||||
|
|
||||||
|
blank=format_overwrite uses "DVD-RW Quick" formatting (MMC-type 15h)
|
||||||
|
and writes a first session of 128 MB. This leads to media which are expandable
|
||||||
|
and random addressable by cdrskin.
|
||||||
|
|
||||||
|
blank=format_overwrite_quickest uses "DVD-RW Quick" formatting (type 15h) too,
|
||||||
|
but leaves the media in "intermediate" state. In the first session of writing
|
||||||
|
one may only write sequentially to such a DVD. After that, it gets random
|
||||||
|
addressable by cdrskin. DVD-ROM drives might show ill behavior with them.
|
||||||
|
|
||||||
|
blank=format_overwrite_full uses preferrably "Full Format" (type 00h).
|
||||||
|
This formatting lasts as long as writing a full DVD. It includes writing of
|
||||||
|
lead-out which is said to be good for DVD ROM compatibility.
|
||||||
|
|
||||||
|
De-formatting options are available to make overwriteable DVD-RW sequential:
|
||||||
|
|
||||||
|
blank=deformat_sequential performs thorough blanking of all states of DVD-RW.
|
||||||
|
blank=all and blank=fast perform the same thorough blanking, but refuse to do
|
||||||
|
this with overwriteable DVD-RW, thus preserving their formatting. The specs
|
||||||
|
allow minimal blanking but the resulting media on my drives offer no
|
||||||
|
Incremental Streaming afterwards. So blank=fast will do full blanking.
|
||||||
|
|
||||||
|
blank=deformat_sequential_quickest is faster but might yield DAO-only media.
|
||||||
|
|
||||||
|
|
||||||
Special compilation variations
|
Special compilation variations
|
||||||
|
|
||||||
You may get a (super fat) statically linked binary by :
|
You may get a (super fat) statically linked binary by :
|
||||||
|
226
cdrskin/add_ts_changes_to_libburn_0_3_2
Executable file
226
cdrskin/add_ts_changes_to_libburn_0_3_2
Executable file
@ -0,0 +1,226 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# This script documents how this cdrskin version was derived from
|
||||||
|
# a vanilla libburn version. It is not intended nor needed for any
|
||||||
|
# use of cdrskin but included here only to show the technical
|
||||||
|
# relationship between both projects - which are close friends
|
||||||
|
# and issue roughly the same software.
|
||||||
|
#
|
||||||
|
# Package maintainers are advised to cover rather libburn than
|
||||||
|
# cdrskin unless they put only emphasis on the cdrecord emulation
|
||||||
|
# provided by cdrskin. libburn contains cdrskin - cdrskin is an
|
||||||
|
# oscillating, friendly and coordinated fork of libburn.
|
||||||
|
#
|
||||||
|
# Script results are a source tarball and two binaries
|
||||||
|
# one dynamic and one static in respect to system libs.
|
||||||
|
# Both binaries are static in respect to libburn.
|
||||||
|
#
|
||||||
|
# The script is to be run in the directory above the toplevel
|
||||||
|
# directory of libburn resp. cdrskin development.
|
||||||
|
#
|
||||||
|
|
||||||
|
# The top level directory in the SVN snapshot is named
|
||||||
|
intermediate="./libburn_pykix"
|
||||||
|
|
||||||
|
# libburn source used: http://libburnia.pykix.org
|
||||||
|
# Downloaded by:
|
||||||
|
# $ svn co http://libburnia-svn.pykix.org/libburn/tags/... $intermediate
|
||||||
|
# packed up in a tarball just to save it from inadverted changes by
|
||||||
|
# $ tar czf libburn_svn.tgz $intermediate
|
||||||
|
original="./libburn_svn_release.tgz"
|
||||||
|
# Historic moments:
|
||||||
|
# original="./libburn_svn_A60815.tgz"
|
||||||
|
# original="./libburn_cdrskin_A60819.tgz"
|
||||||
|
|
||||||
|
|
||||||
|
# My changes are in $changes , mainly in $changes/cdrskin
|
||||||
|
changes="./libburn-release"
|
||||||
|
|
||||||
|
skin_release="0.3.2"
|
||||||
|
patch_level=".pl00"
|
||||||
|
skin_rev="$skin_release""$patch_level"
|
||||||
|
|
||||||
|
# The result directory and the name of the result tarballs
|
||||||
|
target="./cdrskin-${skin_release}"
|
||||||
|
cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz"
|
||||||
|
cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz"
|
||||||
|
|
||||||
|
# (This once earned me an embarrassingly blooping source tarball)
|
||||||
|
# compile_dir="$changes"
|
||||||
|
|
||||||
|
compile_dir="$target"
|
||||||
|
compile_cmd="./cdrskin/compile_cdrskin.sh"
|
||||||
|
compile_static_opts="-static"
|
||||||
|
compile_result="cdrskin/cdrskin"
|
||||||
|
|
||||||
|
man_to_html_cmd="./cdrskin/convert_man_to_html.sh"
|
||||||
|
man_page_html="cdrskin/man_1_cdrskin.html"
|
||||||
|
|
||||||
|
bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0"
|
||||||
|
bintarget_static="$bintarget_dynamic"-static
|
||||||
|
|
||||||
|
if test -d "$changes"
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo "$0 : FATAL : no directory $changes" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in "$target" "$intermediate"
|
||||||
|
do
|
||||||
|
if test -e "$i"
|
||||||
|
then
|
||||||
|
echo "$0 : FATAL : already existing $i" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -f "$original"
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo "$0 : FATAL : no file $original" >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Unpack SVN snapshot.
|
||||||
|
tar xzf "$original"
|
||||||
|
|
||||||
|
|
||||||
|
# Rename the directory to the cdrskin name
|
||||||
|
mv "$intermediate" "$target"
|
||||||
|
|
||||||
|
|
||||||
|
# Copy the changes from the development tree
|
||||||
|
#
|
||||||
|
cdrskin_dir="$changes"/cdrskin
|
||||||
|
libburn_dir="$changes"/libburn
|
||||||
|
cdrskin_target="$target"/cdrskin
|
||||||
|
libburn_target="$target"/libburn
|
||||||
|
|
||||||
|
# Create version timestamp
|
||||||
|
timestamp="$(date -u '+%Y.%m.%d.%H%M%S')"
|
||||||
|
echo "$timestamp"
|
||||||
|
echo '#define Cdrskin_timestamP "'"$timestamp"'"' >"$cdrskin_dir"/cdrskin_timestamp.h
|
||||||
|
|
||||||
|
# Add the cdrskin files
|
||||||
|
if test -e "$cdrskin_target"
|
||||||
|
then
|
||||||
|
rm -rf "$cdrskin_target"
|
||||||
|
fi
|
||||||
|
cp -a "$cdrskin_dir" "$cdrskin_target"
|
||||||
|
|
||||||
|
# Remove copied binaries
|
||||||
|
rm "$cdrskin_target"/*.o
|
||||||
|
rm "$cdrskin_target"/cdrfifo
|
||||||
|
rm "$cdrskin_target"/cdrskin
|
||||||
|
rm "$cdrskin_target"/cleanup
|
||||||
|
for i in std new make old
|
||||||
|
do
|
||||||
|
if test -e "$cdrskin_target"/cdrskin_"$i"
|
||||||
|
then
|
||||||
|
rm "$cdrskin_target"/cdrskin_"$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove eventual SVN stuff from cdrskin directory
|
||||||
|
for i in .deps .dirstamp .libs
|
||||||
|
do
|
||||||
|
if test -e "$cdrskin_target"/"$i"
|
||||||
|
then
|
||||||
|
rm -rf "$cdrskin_target"/"$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove GIFs of cdrskin_eng.html
|
||||||
|
rm "$cdrskin_target"/doener_*.gif
|
||||||
|
|
||||||
|
# Remove automatically generated HTML man page
|
||||||
|
rm "$cdrskin_target"/man_1_cdrskin.html
|
||||||
|
|
||||||
|
# Remove all add_ts_changes_to_libburn besides this one
|
||||||
|
for i in "$cdrskin_target"/add_ts_changes_to_libburn*
|
||||||
|
do
|
||||||
|
if test $(basename "$0") = $(basename "$i")
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
rm $i
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
||||||
|
for i in "$target"/.svn "$target"/*/.svn
|
||||||
|
do
|
||||||
|
if test "$i" = "$target"'/*/.svn'
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
if test -e "$i"
|
||||||
|
then
|
||||||
|
rm -rf "$i"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Make SVN state tarball for the libburn team
|
||||||
|
tar czf "$cdrskin_tarball_svn" "$target"
|
||||||
|
|
||||||
|
|
||||||
|
# Get over dependecy on autotools. Rely only on cc, make et. al.
|
||||||
|
# This is not the same as "make dist" but i can do it without
|
||||||
|
# having to evaluate the quality of said "make dist"
|
||||||
|
#
|
||||||
|
( cd "$target" ; ./bootstrap )
|
||||||
|
|
||||||
|
# Remove unwanted stuff after bootstrap
|
||||||
|
for i in "$target"/autom4te.cache
|
||||||
|
do
|
||||||
|
if echo "$i" | grep '\*' >/dev/null
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
if test -e "$i"
|
||||||
|
then
|
||||||
|
rm -rf "$i"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Pack it up to the new libburn+cdrskin-tarball
|
||||||
|
tar czf "$cdrskin_tarball" "$target"
|
||||||
|
|
||||||
|
# Produce a static and a dynamic binary, and a HTML man page
|
||||||
|
(
|
||||||
|
cd "$compile_dir" || exit 1
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
"$compile_cmd" -do_strip
|
||||||
|
cp "$compile_result" "../$bintarget_dynamic"
|
||||||
|
if test -n "$compile_static_opts"
|
||||||
|
then
|
||||||
|
"$compile_cmd" $compile_static_opts -do_strip
|
||||||
|
cp "$compile_result" "../$bintarget_static"
|
||||||
|
fi
|
||||||
|
"$man_to_html_cmd"
|
||||||
|
mv "$man_page_html" ..
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove the build area
|
||||||
|
# Disable this for debugging the merge process
|
||||||
|
rm -rf "$target"
|
||||||
|
|
||||||
|
# Show the result
|
||||||
|
./"$bintarget_dynamic" -version
|
||||||
|
./"$bintarget_static" -version
|
||||||
|
ls -l "$cdrskin_tarball"
|
||||||
|
ls -l "$bintarget_dynamic"
|
||||||
|
ls -l "$bintarget_static"
|
||||||
|
ls -l $(basename "$man_page_html")
|
||||||
|
|
@ -20,21 +20,30 @@ set -x
|
|||||||
# The script is to be run in the directory above the toplevel
|
# The script is to be run in the directory above the toplevel
|
||||||
# directory of libburn resp. cdrskin development.
|
# directory of libburn resp. cdrskin development.
|
||||||
#
|
#
|
||||||
# libburn version used: http://libburn.pykix.org SVN of Aug 15 2006
|
|
||||||
# packed up in a tarball just to save it from inadverted changes.
|
|
||||||
# original="./libburn_svn_A60815.tgz"
|
|
||||||
original="./libburn_cdrskin_A60819.tgz"
|
|
||||||
|
|
||||||
# The top level directory in that snapshot is named
|
# The top level directory in the SVN snapshot is named
|
||||||
intermediate="./libburn_pykix"
|
intermediate="./libburn_pykix"
|
||||||
|
|
||||||
# My changes are in libburn-0.2.1.ts.develop , mainly in ./cdrskin
|
# libburn source used: http://libburnia.pykix.org
|
||||||
|
# Downloaded by:
|
||||||
|
# $ svn co http://libburnia-svn.pykix.org/libburn/tags/... $intermediate
|
||||||
|
# packed up in a tarball just to save it from inadverted changes by
|
||||||
|
# $ tar czf libburn_svn.tgz $intermediate
|
||||||
|
original="./libburn_svn.tgz"
|
||||||
|
# Historic moments:
|
||||||
|
# original="./libburn_svn_A60815.tgz"
|
||||||
|
# original="./libburn_cdrskin_A60819.tgz"
|
||||||
|
|
||||||
changes="./libburn-0.2.1.ts.develop"
|
|
||||||
skin_rev="0.1.5"
|
# My changes are in $changes , mainly in $changes/cdrskin
|
||||||
|
changes="./libburn-develop"
|
||||||
|
|
||||||
|
skin_release="0.3.3"
|
||||||
|
patch_level=""
|
||||||
|
skin_rev="$skin_release""$patch_level"
|
||||||
|
|
||||||
# The result directory and the name of the result tarballs
|
# The result directory and the name of the result tarballs
|
||||||
target="./cdrskin-${skin_rev}"
|
target="./cdrskin-${skin_release}"
|
||||||
cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz"
|
cdrskin_tarball="./cdrskin-${skin_rev}.tar.gz"
|
||||||
cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz"
|
cdrskin_tarball_svn="./cdrskin-${skin_rev}.svn.tar.gz"
|
||||||
|
|
||||||
@ -46,9 +55,10 @@ compile_cmd="./cdrskin/compile_cdrskin.sh"
|
|||||||
compile_static_opts="-static"
|
compile_static_opts="-static"
|
||||||
compile_result="cdrskin/cdrskin"
|
compile_result="cdrskin/cdrskin"
|
||||||
|
|
||||||
|
man_to_html_cmd="./cdrskin/convert_man_to_html.sh"
|
||||||
|
man_page_html="cdrskin/man_1_cdrskin.html"
|
||||||
|
|
||||||
# addresses relative to compile_dir :
|
bintarget_dynamic="cdrskin_${skin_rev}-x86-suse9_0"
|
||||||
bintarget_dynamic="../cdrskin_${skin_rev}-x86-suse9_0"
|
|
||||||
bintarget_static="$bintarget_dynamic"-static
|
bintarget_static="$bintarget_dynamic"-static
|
||||||
|
|
||||||
if test -d "$changes"
|
if test -d "$changes"
|
||||||
@ -105,9 +115,43 @@ fi
|
|||||||
cp -a "$cdrskin_dir" "$cdrskin_target"
|
cp -a "$cdrskin_dir" "$cdrskin_target"
|
||||||
|
|
||||||
# Remove copied binaries
|
# Remove copied binaries
|
||||||
|
rm "$cdrskin_target"/*.o
|
||||||
rm "$cdrskin_target"/cdrfifo
|
rm "$cdrskin_target"/cdrfifo
|
||||||
rm "$cdrskin_target"/cdrskin
|
rm "$cdrskin_target"/cdrskin
|
||||||
rm "$cdrskin_target"/cleanup
|
rm "$cdrskin_target"/cleanup
|
||||||
|
for i in std new make old
|
||||||
|
do
|
||||||
|
if test -e "$cdrskin_target"/cdrskin_"$i"
|
||||||
|
then
|
||||||
|
rm "$cdrskin_target"/cdrskin_"$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove eventual SVN stuff from cdrskin directory
|
||||||
|
for i in .deps .dirstamp .libs
|
||||||
|
do
|
||||||
|
if test -e "$cdrskin_target"/"$i"
|
||||||
|
then
|
||||||
|
rm -rf "$cdrskin_target"/"$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove GIFs of cdrskin_eng.html
|
||||||
|
rm "$cdrskin_target"/doener_*.gif
|
||||||
|
|
||||||
|
# Remove automatically generated HTML man page
|
||||||
|
rm "$cdrskin_target"/man_1_cdrskin.html
|
||||||
|
|
||||||
|
# Remove all add_ts_changes_to_libburn besides this one
|
||||||
|
for i in "$cdrskin_target"/add_ts_changes_to_libburn*
|
||||||
|
do
|
||||||
|
if test $(basename "$0") = $(basename "$i")
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
rm $i
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
# Remove unwanted SVN stuff (TODO: avoid downloading it)
|
||||||
for i in "$target"/.svn "$target"/*/.svn
|
for i in "$target"/.svn "$target"/*/.svn
|
||||||
@ -123,15 +167,8 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# For now: Add own libburn-README in toplevel
|
|
||||||
cp -a "$changes"/README "$target"
|
|
||||||
|
|
||||||
# Add modified Makefile.am
|
|
||||||
cp -a "$changes"/Makefile.am "$target"
|
|
||||||
|
|
||||||
|
|
||||||
# Make SVN state tarball for the libburn team
|
# Make SVN state tarball for the libburn team
|
||||||
# TODO: will probably be obsoleted after sucessful merge
|
|
||||||
tar czf "$cdrskin_tarball_svn" "$target"
|
tar czf "$cdrskin_tarball_svn" "$target"
|
||||||
|
|
||||||
|
|
||||||
@ -141,25 +178,49 @@ tar czf "$cdrskin_tarball_svn" "$target"
|
|||||||
#
|
#
|
||||||
( cd "$target" ; ./bootstrap )
|
( cd "$target" ; ./bootstrap )
|
||||||
|
|
||||||
|
# Remove unwanted stuff after bootstrap
|
||||||
|
for i in "$target"/autom4te.cache
|
||||||
|
do
|
||||||
|
if echo "$i" | grep '\*' >/dev/null
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
if test -e "$i"
|
||||||
|
then
|
||||||
|
rm -rf "$i"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
# Pack it up to the new libburn+cdrskin-tarball
|
# Pack it up to the new libburn+cdrskin-tarball
|
||||||
tar czf "$cdrskin_tarball" "$target"
|
tar czf "$cdrskin_tarball" "$target"
|
||||||
|
|
||||||
# Produce a static and a dynamic binary
|
# Produce a static and a dynamic binary, and a HTML man page
|
||||||
(
|
(
|
||||||
cd "$compile_dir" || exit 1
|
cd "$compile_dir" || exit 1
|
||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
$compile_cmd -do_strip
|
"$compile_cmd" -do_strip
|
||||||
cp "$compile_result" "$bintarget_dynamic"
|
cp "$compile_result" "../$bintarget_dynamic"
|
||||||
if test -n "$compile_static_opts"
|
if test -n "$compile_static_opts"
|
||||||
then
|
then
|
||||||
$compile_cmd $compile_static_opts -do_strip
|
"$compile_cmd" $compile_static_opts -do_strip
|
||||||
cp "$compile_result" "$bintarget_static"
|
cp "$compile_result" "../$bintarget_static"
|
||||||
fi
|
fi
|
||||||
|
"$man_to_html_cmd"
|
||||||
|
mv "$man_page_html" ..
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Remove the build area
|
||||||
# Disable this for debugging the merge process
|
# Disable this for debugging the merge process
|
||||||
rm -rf "$target"
|
rm -rf "$target"
|
||||||
|
|
||||||
|
# Show the result
|
||||||
|
./"$bintarget_dynamic" -version
|
||||||
|
./"$bintarget_static" -version
|
||||||
|
ls -l "$cdrskin_tarball"
|
||||||
|
ls -l "$bintarget_dynamic"
|
||||||
|
ls -l "$bintarget_static"
|
||||||
|
ls -l $(basename "$man_page_html")
|
||||||
|
|
@ -48,6 +48,9 @@ struct CdrfifO {
|
|||||||
int source_fd;
|
int source_fd;
|
||||||
double in_counter;
|
double in_counter;
|
||||||
|
|
||||||
|
double fd_in_counter;
|
||||||
|
double fd_in_limit;
|
||||||
|
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
int buffer_is_full;
|
int buffer_is_full;
|
||||||
@ -72,17 +75,27 @@ struct CdrfifO {
|
|||||||
double empty_counter;
|
double empty_counter;
|
||||||
double full_counter;
|
double full_counter;
|
||||||
|
|
||||||
|
|
||||||
/* (sequential) fd chaining */
|
/* (sequential) fd chaining */
|
||||||
|
/* fds: 0=source, 1=dest */
|
||||||
int follow_up_fds[Cdrfifo_ffd_maX][2];
|
int follow_up_fds[Cdrfifo_ffd_maX][2];
|
||||||
|
|
||||||
/* index of first byte in buffer which does not belong to predecessor fd */
|
/* index of first byte in buffer which does not belong to predecessor fd */
|
||||||
int follow_up_eop[Cdrfifo_ffd_maX];
|
int follow_up_eop[Cdrfifo_ffd_maX];
|
||||||
|
|
||||||
/* index of first byte in buffer which belongs to [this] fd pair */
|
/* index of first byte in buffer which belongs to [this] fd pair */
|
||||||
int follow_up_sod[Cdrfifo_ffd_maX];
|
int follow_up_sod[Cdrfifo_ffd_maX];
|
||||||
|
|
||||||
|
/* values for fd_in_limit */
|
||||||
|
double follow_up_in_limits[Cdrfifo_ffd_maX];
|
||||||
|
|
||||||
/* number of defined follow-ups */
|
/* number of defined follow-ups */
|
||||||
int follow_up_fd_counter;
|
int follow_up_fd_counter;
|
||||||
|
|
||||||
/* index of currently active (i.e. reading) follow-up */
|
/* index of currently active (i.e. reading) follow-up */
|
||||||
int follow_up_fd_idx;
|
int follow_up_fd_idx;
|
||||||
|
|
||||||
|
|
||||||
/* (simultaneous) peer chaining */
|
/* (simultaneous) peer chaining */
|
||||||
struct CdrfifO *next;
|
struct CdrfifO *next;
|
||||||
struct CdrfifO *prev;
|
struct CdrfifO *prev;
|
||||||
@ -117,6 +130,8 @@ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd,
|
|||||||
buffer_size+= chunk_size-(buffer_size%chunk_size);
|
buffer_size+= chunk_size-(buffer_size%chunk_size);
|
||||||
o->source_fd= source_fd;
|
o->source_fd= source_fd;
|
||||||
o->in_counter= 0.0;
|
o->in_counter= 0.0;
|
||||||
|
o->fd_in_counter= 0;
|
||||||
|
o->fd_in_limit= -1.0;
|
||||||
o->buffer= NULL;
|
o->buffer= NULL;
|
||||||
o->buffer_is_full= 0;
|
o->buffer_is_full= 0;
|
||||||
o->buffer_size= buffer_size;
|
o->buffer_size= buffer_size;
|
||||||
@ -140,6 +155,7 @@ int Cdrfifo_new(struct CdrfifO **ff, int source_fd, int dest_fd,
|
|||||||
for(i= 0; i<Cdrfifo_ffd_maX; i++) {
|
for(i= 0; i<Cdrfifo_ffd_maX; i++) {
|
||||||
o->follow_up_fds[i][0]= o->follow_up_fds[i][1]= -1;
|
o->follow_up_fds[i][0]= o->follow_up_fds[i][1]= -1;
|
||||||
o->follow_up_eop[i]= o->follow_up_sod[i]= -1;
|
o->follow_up_eop[i]= o->follow_up_sod[i]= -1;
|
||||||
|
o->follow_up_in_limits[i]= -1.0;
|
||||||
}
|
}
|
||||||
o->follow_up_fd_counter= 0;
|
o->follow_up_fd_counter= 0;
|
||||||
o->follow_up_fd_idx= -1;
|
o->follow_up_fd_idx= -1;
|
||||||
@ -224,6 +240,26 @@ int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Set a fixed size for input in order to cut off any unwanted tail
|
||||||
|
@param o The fifo object
|
||||||
|
@param idx index for fds attached via Cdrfifo_attach_follow_up_fds(),
|
||||||
|
first attached is 0, <0 directs limit to active fd limit
|
||||||
|
(i.e. first track is -1, second track is 0, third is 1, ...)
|
||||||
|
*/
|
||||||
|
int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
if(idx<0) {
|
||||||
|
o->fd_in_limit= fd_in_limit;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
if(idx >= o->follow_up_fd_counter)
|
||||||
|
return(0);
|
||||||
|
o->follow_up_in_limits[idx]= fd_in_limit;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag)
|
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag)
|
||||||
{
|
{
|
||||||
o->source_fd= source_fd;
|
o->source_fd= source_fd;
|
||||||
@ -244,6 +280,7 @@ int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag)
|
|||||||
fifo buffer when its predecessors are exhausted. Reading will start as
|
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||||
soon as reading of the predecessor encounters EOF. Writing will start
|
soon as reading of the predecessor encounters EOF. Writing will start
|
||||||
as soon as all pending predecessor data are written.
|
as soon as all pending predecessor data are written.
|
||||||
|
@return index number of new item + 1, <=0 indicates error
|
||||||
*/
|
*/
|
||||||
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||||
int flag)
|
int flag)
|
||||||
@ -253,7 +290,7 @@ int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
|||||||
o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd;
|
o->follow_up_fds[o->follow_up_fd_counter][0]= source_fd;
|
||||||
o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd;
|
o->follow_up_fds[o->follow_up_fd_counter][1]= dest_fd;
|
||||||
o->follow_up_fd_counter++;
|
o->follow_up_fd_counter++;
|
||||||
return(1);
|
return(o->follow_up_fd_counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -567,7 +604,12 @@ after_write:;
|
|||||||
can_read= o->chunk_size;
|
can_read= o->chunk_size;
|
||||||
if(o->write_idx<o->read_idx && o->write_idx+can_read > o->read_idx)
|
if(o->write_idx<o->read_idx && o->write_idx+can_read > o->read_idx)
|
||||||
can_read= o->read_idx - o->write_idx;
|
can_read= o->read_idx - o->write_idx;
|
||||||
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
if(o->fd_in_limit>=0.0)
|
||||||
|
if(can_read > o->fd_in_limit - o->fd_in_counter)
|
||||||
|
can_read= o->fd_in_limit - o->fd_in_counter;
|
||||||
|
ret= 0;
|
||||||
|
if(can_read>0)
|
||||||
|
ret= read(o->source_fd,o->buffer+o->write_idx,can_read);
|
||||||
if(ret==-1) {
|
if(ret==-1) {
|
||||||
|
|
||||||
/* >>> handle input error */;
|
/* >>> handle input error */;
|
||||||
@ -599,6 +641,8 @@ after_write:;
|
|||||||
sod= 0;
|
sod= 0;
|
||||||
o->follow_up_sod[idx]= sod;
|
o->follow_up_sod[idx]= sod;
|
||||||
o->write_idx= sod;
|
o->write_idx= sod;
|
||||||
|
o->fd_in_counter= 0;
|
||||||
|
o->fd_in_limit= o->follow_up_in_limits[idx];
|
||||||
if(Cdrfifo_debuG || (flag&1))
|
if(Cdrfifo_debuG || (flag&1))
|
||||||
fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd);
|
fprintf(stderr,"\ncdrfio: new fifo source fd : %d\n",o->source_fd);
|
||||||
} else {
|
} else {
|
||||||
@ -608,6 +652,7 @@ after_write:;
|
|||||||
did_work= 1;
|
did_work= 1;
|
||||||
o->put_counter++;
|
o->put_counter++;
|
||||||
o->in_counter+= ret;
|
o->in_counter+= ret;
|
||||||
|
o->fd_in_counter+= ret;
|
||||||
o->write_idx+= ret;
|
o->write_idx+= ret;
|
||||||
if(o->write_idx>=o->buffer_size)
|
if(o->write_idx>=o->buffer_size)
|
||||||
o->write_idx= 0;
|
o->write_idx= 0;
|
||||||
@ -751,7 +796,7 @@ ex:;
|
|||||||
|
|
||||||
|
|
||||||
/** Fill the fifo as far as possible without writing to destination fd */
|
/** Fill the fifo as far as possible without writing to destination fd */
|
||||||
int Cdrfifo_fill(struct CdrfifO *o, int flag)
|
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag)
|
||||||
{
|
{
|
||||||
int ret,fill= 0,space,state;
|
int ret,fill= 0,space,state;
|
||||||
|
|
||||||
@ -765,6 +810,8 @@ int Cdrfifo_fill(struct CdrfifO *o, int flag)
|
|||||||
} else if(state!=1)
|
} else if(state!=1)
|
||||||
break;
|
break;
|
||||||
if(space<=0)
|
if(space<=0)
|
||||||
|
break;
|
||||||
|
if(size>=0 && fill>=size)
|
||||||
break;
|
break;
|
||||||
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
|
ret= Cdrfifo_try_to_work(o,100000,NULL,NULL,2);
|
||||||
if(ret<0) {
|
if(ret<0) {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/** The fifo buffer which will smoothen the data stream from data provider
|
/** The fifo buffer which will smoothen the data stream from data provider
|
||||||
to data consumer. Although this is not a mandatory lifesavier for modern
|
to data consumer. Although this is not a mandatory lifesaver for modern
|
||||||
burners any more, a fifo can speed up burning of data which is delivered
|
burners any more, a fifo can speed up burning of data which is delivered
|
||||||
with varying bandwidths (e.g. compressed archives created on the fly
|
with varying bandwidths (e.g. compressed archives created on the fly
|
||||||
or mkisofs running at its speed limit.).
|
or mkisofs running at its speed limit.).
|
||||||
@ -64,6 +64,16 @@ int Cdrfifo_get_sizes(struct CdrfifO *o, int *chunk_size, int *buffer_size,
|
|||||||
int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
int Cdrfifo_set_speed_limit(struct CdrfifO *o, double bytes_per_second,
|
||||||
int flag);
|
int flag);
|
||||||
|
|
||||||
|
/** Set a fixed size for input in order to cut off any unwanted tail
|
||||||
|
@param o The fifo object
|
||||||
|
@param idx index for fds attached via Cdrfifo_attach_follow_up_fds(),
|
||||||
|
first attached is 0, <0 directs limit to active fd limit
|
||||||
|
(i.e. first track is -1, second track is 0, third is 1, ...)
|
||||||
|
*/
|
||||||
|
int Cdrfifo_set_fd_in_limit(struct CdrfifO *o, double fd_in_limit, int idx,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag);
|
int Cdrfifo_set_fds(struct CdrfifO *o, int source_fd, int dest_fd, int flag);
|
||||||
int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag);
|
int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag);
|
||||||
|
|
||||||
@ -72,6 +82,7 @@ int Cdrfifo_get_fds(struct CdrfifO *o, int *source_fd, int *dest_fd, int flag);
|
|||||||
fifo buffer when its predecessors are exhausted. Reading will start as
|
fifo buffer when its predecessors are exhausted. Reading will start as
|
||||||
soon as reading of the predecessor encounters EOF. Writing will start
|
soon as reading of the predecessor encounters EOF. Writing will start
|
||||||
as soon as all pending predecessor data are written.
|
as soon as all pending predecessor data are written.
|
||||||
|
@return index number of new item + 1, <=0 indicates error
|
||||||
*/
|
*/
|
||||||
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
int Cdrfifo_attach_follow_up_fds(struct CdrfifO *o, int source_fd, int dest_fd,
|
||||||
int flag);
|
int flag);
|
||||||
@ -134,10 +145,11 @@ int Cdrfifo_get_cdr_counters(struct CdrfifO *o,
|
|||||||
int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec,
|
int Cdrfifo_try_to_work(struct CdrfifO *o, int wait_usec,
|
||||||
char *reply_buffer, int *reply_count, int flag);
|
char *reply_buffer, int *reply_count, int flag);
|
||||||
|
|
||||||
/** Fill the fifo as far as possible without writing to destination fd
|
/** Fill the fifo as far as possible without writing to destination fd.
|
||||||
|
@param size if >=0 : end filling after the given number of bytes
|
||||||
@return 1 on success, <=0 on failure
|
@return 1 on success, <=0 on failure
|
||||||
*/
|
*/
|
||||||
int Cdrfifo_fill(struct CdrfifO *o, int flag);
|
int Cdrfifo_fill(struct CdrfifO *o, int size, int flag);
|
||||||
|
|
||||||
|
|
||||||
#endif /* Cdrfifo_headerfile_includeD */
|
#endif /* Cdrfifo_headerfile_includeD */
|
||||||
|
809
cdrskin/cdrskin.1
Normal file
809
cdrskin/cdrskin.1
Normal file
@ -0,0 +1,809 @@
|
|||||||
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
|
.\" First parameter, NAME, should be all caps
|
||||||
|
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||||
|
.\" other parameters are allowed: see man(7), man(1)
|
||||||
|
.TH CDRSKIN 1 "February 8, 2007"
|
||||||
|
.\" Please adjust this date whenever revising the manpage.
|
||||||
|
.\"
|
||||||
|
.\" Some roff macros, for reference:
|
||||||
|
.\" .nh disable hyphenation
|
||||||
|
.\" .hy enable hyphenation
|
||||||
|
.\" .ad l left justify
|
||||||
|
.\" .ad b justify to both left and right margins
|
||||||
|
.\" .nf disable filling
|
||||||
|
.\" .fi enable filling
|
||||||
|
.\" .br insert line break
|
||||||
|
.\" .sp <n> insert n+1 empty lines
|
||||||
|
.\" for manpage-specific macros, see man(7)
|
||||||
|
.SH NAME
|
||||||
|
cdrskin \- burns preformatted data to CD-R, CD-RW, DVD-R, DVD-RW, DVD+RW,
|
||||||
|
DVD-RAM via libburn.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B cdrskin
|
||||||
|
.RI [ options | track_source_addresses ]
|
||||||
|
.br
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
|
||||||
|
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
|
||||||
|
.\" respectively.
|
||||||
|
.PP
|
||||||
|
\fBcdrskin\fP is a program that provides some of cdrecord's options
|
||||||
|
in a compatible way for CD media. With DVD it has its own ways.
|
||||||
|
You do not need to be superuser for its daily usage.
|
||||||
|
.PP
|
||||||
|
.B Overview of features:
|
||||||
|
.br
|
||||||
|
Blanking of CD-RW and DVD-RW.
|
||||||
|
.br
|
||||||
|
Burning of data or audio tracks to CD,
|
||||||
|
.br
|
||||||
|
either in versatile Track at Once mode (TAO)
|
||||||
|
.br
|
||||||
|
or in Session at Once mode for seamless tracks.
|
||||||
|
.br
|
||||||
|
Multi session on CD (follow-up sessions in TAO only)
|
||||||
|
.br
|
||||||
|
and on DVD-RW or DVD-R (in Incremental write mode only).
|
||||||
|
.br
|
||||||
|
Single session on DVD-RW or DVD-R (Disk-at-once)
|
||||||
|
.br
|
||||||
|
or on overwriteable DVD+RW, DVD-RW, DVD-RAM.
|
||||||
|
.br
|
||||||
|
Bus scan, burnfree, speed options, retrieving media info, padding, fifo.
|
||||||
|
.br
|
||||||
|
See section EXAMPLES at the end of this text.
|
||||||
|
.PP
|
||||||
|
.B Track recording model:
|
||||||
|
.br
|
||||||
|
The input-output entities which get processed are called tracks.
|
||||||
|
A \fBtrack\fP stores a stream of bytes.
|
||||||
|
.br
|
||||||
|
Each track is initiated by one track source address argument, which may either
|
||||||
|
be "-" for standard input or the address of a readable file. If no write mode
|
||||||
|
is given explicitely then one will be chosen which matches the peculiarities
|
||||||
|
of track sources and the state of the output media.
|
||||||
|
.PP
|
||||||
|
More than one track can be burned by a single run of cdrskin.
|
||||||
|
In the terms of the MMC standard all tracks written by the same run constitute
|
||||||
|
a \fBsession\fP.
|
||||||
|
.br
|
||||||
|
Some media types can be kept appendable so that further tracks can
|
||||||
|
be written to them in subsequent runs of cdrskin (see option -multi).
|
||||||
|
Info about the addresses of burned tracks is kept in a table of
|
||||||
|
content (TOC) on media and can be retrieved via cdrskin option -toc.
|
||||||
|
These informations are also used by the operating systems' CD-ROM read drivers.
|
||||||
|
.PP
|
||||||
|
In general there are two types of tracks: data and audio. They differ in
|
||||||
|
sector size, throughput and readability via the systems' CD-ROM drivers
|
||||||
|
resp. by music CD players. With DVD there is only type data.
|
||||||
|
.br
|
||||||
|
If not explicitely option -audio is given, then any track is burned as type
|
||||||
|
data, unless the track source is a file with suffix ".wav" or ".au" and has a
|
||||||
|
header part which identifies it as MS-WAVE resp. SUN Audio with suitable
|
||||||
|
parameters. Such files are burned as audio tracks by default.
|
||||||
|
.PP
|
||||||
|
While audio tracks just contain a given time span of acoustic vibrations,
|
||||||
|
data tracks may have an arbitray meaning. Nevertheless, ISO-9660 filesystems
|
||||||
|
are established as a format which can represent a tree of directories and
|
||||||
|
files on all major operating systems. Such filesystem images can be
|
||||||
|
produced by programs mkisofs or genisoimage. They can also be extended by
|
||||||
|
follow-up tracks if prepared properly. See the man pages of said programs.
|
||||||
|
cdrskin is able to fulfill the needs about their option -C.
|
||||||
|
.br
|
||||||
|
Another type of data track content are archive formats which originally
|
||||||
|
have been developed for magnetic tapes. Only formats which mark a detectable
|
||||||
|
end-of-archive in their data are suitable, though. Well tested are
|
||||||
|
the archivers afio and star. Not suitable seems GNU tar.
|
||||||
|
.PP
|
||||||
|
.B Recordable CD Media:
|
||||||
|
.br
|
||||||
|
CD-R can be initially written only once and eventually extended until they
|
||||||
|
get closed (or are spoiled because they are overly full). After that they are
|
||||||
|
read-only. Closing is done automatically unless option
|
||||||
|
.B -multi
|
||||||
|
is given which keeps the media appendable.
|
||||||
|
.br
|
||||||
|
There are two write modes,
|
||||||
|
.B -tao
|
||||||
|
and
|
||||||
|
.B -sao .
|
||||||
|
.br
|
||||||
|
-tao allows to use track source of unpredictable length (like stdin) and allows
|
||||||
|
to write further sessions to appendable media. -sao produces audio sessions
|
||||||
|
with seamless tracks but needs predicted track sizes and cannot append sessions
|
||||||
|
to media.
|
||||||
|
.br
|
||||||
|
CD-RW media can be blanked to make them re-usable for another
|
||||||
|
round of overwriting. Usually
|
||||||
|
.B blank=fast
|
||||||
|
is the appropriate option.
|
||||||
|
Blanking damages the previous content but does not
|
||||||
|
make it completely unreadable. It is no effective privacy precaution.
|
||||||
|
Multiple cycles of blanking and overwriting with random numbers might be.
|
||||||
|
.PP
|
||||||
|
.B Sequentially Recordable DVD Media:
|
||||||
|
.br
|
||||||
|
Currently only DVD-RW and DVD-R can be used for the Sequential recording model.
|
||||||
|
DVD-RW must be in state "Sequential Recording".
|
||||||
|
The media must be either blank or appendable.
|
||||||
|
Newly purchased DVD-RW and DVD-R media are in this state.
|
||||||
|
Used DVD-RW get into blank sequential state by option
|
||||||
|
.B blank=deformat_sequential .
|
||||||
|
.br
|
||||||
|
Incremental Streaming is the default write mode if it is offered by the media.
|
||||||
|
It may be selected explicitely by option
|
||||||
|
.B -tao
|
||||||
|
as it resembles much CD TAO by allowing track sources of
|
||||||
|
unpredicted length and to keep media appendable by option
|
||||||
|
.B -multi .
|
||||||
|
The only restriction towards CD-R[W] is the lack of support for -audio tracks.
|
||||||
|
Multiple tracks per session are permissible.
|
||||||
|
.br
|
||||||
|
The other write mode, DAO, has many restrictions. It does not work with
|
||||||
|
appendable media, allows no -multi and only a single track. The size of the
|
||||||
|
track needs to be known in advance. So either its source has to be a disk file
|
||||||
|
of recognizable size or the size has to be announced explicitely by options
|
||||||
|
.B tsize=
|
||||||
|
or
|
||||||
|
.B tao_to_sao_tsize= .
|
||||||
|
.br
|
||||||
|
DAO is the default mode for media which do not offer feature 21h Incremental
|
||||||
|
Streaming. It may also be selected explicitely by option
|
||||||
|
.B -sao .
|
||||||
|
.br
|
||||||
|
Program growisofs uses DAO on sequential DVD-R[W] media for maximum
|
||||||
|
DVD-ROM/-Video compatibility.
|
||||||
|
.PP
|
||||||
|
.B Overwriteable DVD Media:
|
||||||
|
.br
|
||||||
|
Currently only types DVD+RW, DVD-RW and DVD-RAM can be overwritten via cdrskin.
|
||||||
|
.br
|
||||||
|
DVD+RW and DVD-RAM media get treated as blank media regardless wether they
|
||||||
|
hold data or not. They need no special initial formatting.
|
||||||
|
Options -audio and -multi are not allowed. Only one track is allowed.
|
||||||
|
-toc does not return information about the media content.
|
||||||
|
Currently there is no difference between -sao and -tao. If ever, then -tao
|
||||||
|
will be the mode which preserves the current behavior.
|
||||||
|
.br
|
||||||
|
DVD-RW are sold in state "Sequential Recording". To become suitable for the
|
||||||
|
Overwriteable DVD recording model they need to get formatted to state
|
||||||
|
"Restricted Overwrite". Then they behave much like DVD+RW. This formatting
|
||||||
|
can be done by option
|
||||||
|
.B blank=format_overwrite .
|
||||||
|
.br
|
||||||
|
Several programs like dvd+rw-format, cdrecord, wodim, or cdrskin option
|
||||||
|
blank=deformat_sequential can bring a DVD-RW out of overwriteable state so
|
||||||
|
that it has to be formatted again. If in doubt, just give it a try.
|
||||||
|
.PP
|
||||||
|
.B Drive preparation and addressing:
|
||||||
|
.br
|
||||||
|
The drives, either CD burners or DVD burners, are accessed via addresses which
|
||||||
|
are specific to libburn and the operating system. Those addresses get listed
|
||||||
|
by a run of \fBcdrskin --devices\fP.
|
||||||
|
.br
|
||||||
|
On Linux, they are device files which traditionally do not offer
|
||||||
|
w-permissions for normal users. Because libburn needs rw-permission,
|
||||||
|
it might be only the superuser who is able to get this list without further
|
||||||
|
precautions.
|
||||||
|
.br
|
||||||
|
It is consensus that \fBchmod a+rw /dev/sg0\fP or \fBchmod a+rw /dev/hdc\fP
|
||||||
|
is less security sensitive than chmod u+s /usr/bin/cdrskin. The risk for the
|
||||||
|
drive is somewhat higher but the overall system is much less at stake.
|
||||||
|
.br
|
||||||
|
.PP
|
||||||
|
If you only got one CD capable drive then you may leave out cdrskin option
|
||||||
|
\fBdev=\fP. Else you should use this option to address the drive you want.
|
||||||
|
.br
|
||||||
|
cdrskin option dev= not only accepts the listed addresses but also
|
||||||
|
traditional cdrecord SCSI addresses which on Linux consist of three
|
||||||
|
numbers: Bus,Target,Lun. There is also a related address family "ATA" which
|
||||||
|
accesses IDE drives not under control of Linux SCSI drivers:
|
||||||
|
ATA:Bus,Target,Lun.
|
||||||
|
.br
|
||||||
|
See option -scanbus for getting a list of cdrecord style addresses.
|
||||||
|
.br
|
||||||
|
Further are accepted on Linux: links to libburn-suitable device files,
|
||||||
|
device files which have the same major and minor device number,
|
||||||
|
and device files which have the same SCSI address parameters (e.g. /dev/sr0).
|
||||||
|
.br
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BI \-\-help
|
||||||
|
Show non-cdrecord compatible options.
|
||||||
|
.TP
|
||||||
|
.BI \-help
|
||||||
|
Show cdrecord compatible options.
|
||||||
|
.TP
|
||||||
|
.BI \-version
|
||||||
|
Print cdrskin id line, compatibility lure line, libburn version, cdrskin
|
||||||
|
version, version timestamp, build timestamp (if available), and then exit.
|
||||||
|
.PP
|
||||||
|
Alphabetical list of options which are intended to be compatible with
|
||||||
|
original cdrecord by Joerg Schilling:
|
||||||
|
.TP
|
||||||
|
.BI \-atip
|
||||||
|
Retrieve some info about media state. With CD-RW print "Is erasable".
|
||||||
|
With DVD media print "book type:" and a media type text.
|
||||||
|
.TP
|
||||||
|
.BI \-audio
|
||||||
|
Announces that the subsequent tracks are to be burned as audio.
|
||||||
|
The source is supposed to be uncompressed headerless PCM, 44100 Hz, 16 bit,
|
||||||
|
stereo. For little-endian byte order (which is usual on PCs) use option
|
||||||
|
-swab. Unless marked explicitely by option -data, input files with suffix
|
||||||
|
.wav are examined wether they have a header in MS-WAVE format confirming
|
||||||
|
those parameters and eventually raw audio data get extracted and burned as
|
||||||
|
audio track. Same is done for suffix .au and SUN Audio.
|
||||||
|
.br
|
||||||
|
Option -audio may be used only with CD media and not with DVD.
|
||||||
|
.TP
|
||||||
|
.BI blank= type
|
||||||
|
Blank a CD-RW, a DVD-RW, or format a DVD+/-RW.
|
||||||
|
This is combinable with burning in the same run of cdrskin.
|
||||||
|
The type given with blank= selects the particular behavior:
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
help
|
||||||
|
Print this list of blanking types.
|
||||||
|
.TP
|
||||||
|
all
|
||||||
|
Blank an entire CD-RW or an unformatted DVD-RW.
|
||||||
|
(See also --prodvd_cli_compatible)
|
||||||
|
.TP
|
||||||
|
fast
|
||||||
|
Minimally blank an entire CD-RW or blank an unformatted DVD-RW.
|
||||||
|
(See also --prodvd_cli_compatible)
|
||||||
|
.TP
|
||||||
|
format_overwrite
|
||||||
|
Format a DVD-RW to "Restricted Overwrite". The user should bring some patience.
|
||||||
|
.br
|
||||||
|
(Note: blank=format_overwrite* are not original cdrecord options.)
|
||||||
|
.TP
|
||||||
|
format_overwrite_quickest
|
||||||
|
Like format_overwrite without creating a 128 MB trailblazer session.
|
||||||
|
Leads to "intermediate" state which only allows sequential write
|
||||||
|
beginning from address 0.
|
||||||
|
The "intermediate" state ends after the first session of writing data.
|
||||||
|
.TP
|
||||||
|
format_overwrite_full
|
||||||
|
For DVD-RW this is like format_overwrite but claims full media size
|
||||||
|
rather than just 128 MB.
|
||||||
|
Most traditional formatting is attempted. No data get written.
|
||||||
|
Much patience is required.
|
||||||
|
.br
|
||||||
|
This option treats already formatted media even if not option -force is given.
|
||||||
|
.br
|
||||||
|
For DVD+RW this is the only supported explicit formatting type. It provides
|
||||||
|
complete "de-icing" so no reader slips on unwritten data areas.
|
||||||
|
.TP
|
||||||
|
deformat_sequential
|
||||||
|
Like blank=all but with the additional ability to blank overwriteable DVD-RW.
|
||||||
|
This will destroy their formatting and make them sequentially recordable.
|
||||||
|
.br
|
||||||
|
(Note: blank=deformat_sequential* are not original cdrecord options.)
|
||||||
|
.TP
|
||||||
|
deformat_sequential_quickest
|
||||||
|
Like blank=deformat_sequential but blanking DVD-RW only minimally.
|
||||||
|
This is faster than full blanking but may yield media incapable of
|
||||||
|
Incremental Streaming (-tao).
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
|
.BI \-checkdrive
|
||||||
|
Retrieve some info about the addressed drive.
|
||||||
|
Exits with non-zero value if the drive cannot be found and opened.
|
||||||
|
.TP
|
||||||
|
.BI \-dao
|
||||||
|
Alias for option -sao. Write CD in Session at Once mode
|
||||||
|
or DVD-R[W] in Disc-at-once mode.
|
||||||
|
.TP
|
||||||
|
.BI \-data
|
||||||
|
Subsequent tracks are data tracks. This option is default and only needed
|
||||||
|
to mark the end of the range of an eventual option -audio.
|
||||||
|
.TP
|
||||||
|
.BI dev= target
|
||||||
|
Set the address of the drive to use. Valid are at least the
|
||||||
|
addresses listed with option --devices,
|
||||||
|
X,Y,Z addresses listed with option -scanbus,
|
||||||
|
ATA:X,Y,Z addresses listed with options dev=ATA -scanbus,
|
||||||
|
and volatile libburn drive numbers (numbering starts at "0").
|
||||||
|
Other device file addresses which lead to the same drive might work too.
|
||||||
|
.br
|
||||||
|
If no dev= is given, volatile address "dev=0" is assumed. That is the first
|
||||||
|
drive found being available. Better avoid this ambiguity on systems with more
|
||||||
|
than one drive.
|
||||||
|
.br
|
||||||
|
The special target "help" lists hints about available addressing formats.
|
||||||
|
Be aware that deprecated option --old_pseudo_scsi_adr may change the meaning
|
||||||
|
of Bus,Target,Lun addresses.
|
||||||
|
.TP
|
||||||
|
.BI driveropts= opt
|
||||||
|
Set "driveropts=noburnfree" to disable the drive's eventual protection
|
||||||
|
mechanism against temporary lack of source data (i.e. buffer underrun).
|
||||||
|
A drive that announces no such capabilities will not get them enabled anyway,
|
||||||
|
even if attempted explicitely via "driveropts=burnfree".
|
||||||
|
.TP
|
||||||
|
.BI \-dummy
|
||||||
|
Try to perform the drive operations without actually affecting the inserted
|
||||||
|
media. There is no guarantee that this will work with a particular combination
|
||||||
|
of drive, media, and write mode. Blanking is prevented reliably, though.
|
||||||
|
.TP
|
||||||
|
.BI \-eject
|
||||||
|
Eject the disc after work is done.
|
||||||
|
.TP
|
||||||
|
.BI \-force
|
||||||
|
Assume that the user knows better in situations when cdrskin or libburn are
|
||||||
|
insecure about drive or media state. This includes the attempt to blank
|
||||||
|
media which are classified as unknown or unsuitable, and the attempt to use
|
||||||
|
write modes which libburn believes they are not supported by the drive.
|
||||||
|
.br
|
||||||
|
Another application is with blank=format_* to enforce re-formatting of media
|
||||||
|
which appear to be sufficiently formatted already.
|
||||||
|
.br
|
||||||
|
Use this only when in urgent need.
|
||||||
|
.TP
|
||||||
|
.BI fs= size
|
||||||
|
Set the fifo size to the given value. The value may have appended letters which
|
||||||
|
multiply the preceding number:
|
||||||
|
.br
|
||||||
|
"k" or "K" = 1024 , "m" or "M" = 1024k , "g" or "G" = 1024m , "s" or "S" = 2048
|
||||||
|
.br
|
||||||
|
Set size to 0 in order to disable the fifo (default is "4m").
|
||||||
|
.br
|
||||||
|
The fifo buffers an eventual temporary surplus of track source data in order to
|
||||||
|
provide the drive with a steady stream during times of temporary lack of track
|
||||||
|
source supply.
|
||||||
|
The larger the fifo, the longer periods of poor source supply can be
|
||||||
|
compensated.
|
||||||
|
But a large fifo needs substantial time to fill up if not curbed via
|
||||||
|
option fifo_start_at=size.
|
||||||
|
.TP
|
||||||
|
.BI gracetime= seconds
|
||||||
|
Set the grace time before starting to write. (Default is 0)
|
||||||
|
.TP
|
||||||
|
.BI msifile= path
|
||||||
|
Run option -msinfo and copy the result line into the file given by path.
|
||||||
|
Unlike -msinfo this option does not redirect all normal output away from
|
||||||
|
standard output. But it may be combined with -msinfo to achieve this.
|
||||||
|
.br
|
||||||
|
Note: msifile=path is actually an option of wodim and not of cdrecord.
|
||||||
|
.TP
|
||||||
|
.BI \-msinfo
|
||||||
|
Retrieve multi-session info for preparing a follow-up session by option -C
|
||||||
|
of programs mkisofs or genisoimage. Print result to standard output.
|
||||||
|
This option redirects to stderr all
|
||||||
|
message output besides its own result string, which consists of two numbers.
|
||||||
|
The result string shall be used as argument of option -C with said programs.
|
||||||
|
It gives the start address of the most recent session and the predicted
|
||||||
|
start address of the next session to be appended. The string is empty if
|
||||||
|
the most recent session was not written with option -multi.
|
||||||
|
.TP
|
||||||
|
.BI \-multi
|
||||||
|
This option keeps the CD or unformatted DVD-R[W] appendable after the current
|
||||||
|
session has been written.
|
||||||
|
Without it the disc gets closed and may not be written any more - unless it
|
||||||
|
is a -RW and gets blanked which causes loss of its content.
|
||||||
|
.br
|
||||||
|
The following sessions can only be written in -tao mode. -multi is prohibited
|
||||||
|
with overwriteable DVD media and with DVD-R[W] DAO write mode.
|
||||||
|
Option --prodvd_cli_compatible eventually makes -multi tolerable but cannot
|
||||||
|
make it work.
|
||||||
|
.br
|
||||||
|
In order to have all filesystem content accessible, the eventual ISO-9660
|
||||||
|
filesystem of a follow-up
|
||||||
|
session needs to be prepared in a special way by the filesystem formatter
|
||||||
|
program. mkisofs and genisoimage expect particular info about the situation
|
||||||
|
which can be retrieved by cdrskin option -msinfo.
|
||||||
|
.br
|
||||||
|
To retrieve an archive file which was written as follow-up session,
|
||||||
|
you may use option -toc to learn about the "lba" of the desired track number.
|
||||||
|
.TP
|
||||||
|
.BI \-nopad
|
||||||
|
Do not add trailing zeros to the data stream. Nevertheless, since there seems
|
||||||
|
to be no use for audio tracks with incomplete last sector, this option applies
|
||||||
|
only to data tracks. There it is default.
|
||||||
|
.TP
|
||||||
|
.BI \-pad
|
||||||
|
Add 30 kB of trailing zeros to each data track. (This is not sufficient to
|
||||||
|
avoid problems with various CD-ROM read drivers.)
|
||||||
|
.TP
|
||||||
|
.BI padsize= size
|
||||||
|
Add the given amount of trailing zeros to the next data track. This option
|
||||||
|
gets reset to padsize=0 after that next track is written. It may be set
|
||||||
|
again before the next track argument. About size specifiers, see option fs=.
|
||||||
|
.TP
|
||||||
|
.BI \-raw96r
|
||||||
|
Write CD in RAW/RAW96R mode. This mode allows to put more payload bytes
|
||||||
|
into a CD sector but obviously at the cost of error correction. It can only
|
||||||
|
be used for tracks of fixely predicted size. Some drives allow this mode but
|
||||||
|
then behave strange or even go bad for the next few attempts to burn a CD.
|
||||||
|
One should use it only if inavoidable.
|
||||||
|
.TP
|
||||||
|
.BI \-sao
|
||||||
|
Write CD in Session At Once mode, resp. a sequential DVD-R[W] in Disc-at-once
|
||||||
|
(DAO) mode.
|
||||||
|
.br
|
||||||
|
With CD this mode is able to put several audio tracks on media without
|
||||||
|
producing audible gaps between them.
|
||||||
|
.br
|
||||||
|
With DVD-R[W] this mode can only write a single track.
|
||||||
|
No -multi is allowed with DVD-R[W] -sao.
|
||||||
|
.br
|
||||||
|
-sao can only be used for tracks of fixely predicted size. This implies that
|
||||||
|
track arguments which depict stdin or named pipes need to be preceeded by
|
||||||
|
option tsize= or by option tao_to_sao_tsize=.
|
||||||
|
.br
|
||||||
|
-sao cannot be used on appendable media.
|
||||||
|
.TP
|
||||||
|
.BI \-scanbus
|
||||||
|
Scan the system for drives. On Linux the drives at /dev/s* and at /dev/hd*
|
||||||
|
are to be scanned by two separate runs. One without dev= for /dev/s* and
|
||||||
|
one with dev=ATA for /dev/hd* devices. (Option --drives lists all available
|
||||||
|
drives in a single run.)
|
||||||
|
.br
|
||||||
|
Drives which are busy or which offer no rw-permission to the user of cdrskin
|
||||||
|
are not listed. Busy drives get reported in form of warning messages.
|
||||||
|
.br
|
||||||
|
The useful fields in a result line are:
|
||||||
|
.br
|
||||||
|
Bus,Target,Lun Number) 'Vendor' 'Mode' 'Revision'
|
||||||
|
.TP
|
||||||
|
.BI speed= number
|
||||||
|
Set speed of drive. With data CD, 1x speed corresponds to a throughput of
|
||||||
|
150,000 bytes/second. With DVD, 1x = 1,385,000 bytes/second.
|
||||||
|
It is not an error to set a speed higher than is suitable for drive
|
||||||
|
and media. One should stay within a realistic speed range, though.
|
||||||
|
.TP
|
||||||
|
.BI \-swab
|
||||||
|
Announce that the raw audio data source of subsequent tracks is byte swapped
|
||||||
|
versus the expectations of cdrecord. This option is suitable for audio where
|
||||||
|
the least significant byte of a 16 bit word is first (little-endian, Intel).
|
||||||
|
Most raw audio data on PC systems are available in this byte order.
|
||||||
|
Less guesswork is needed if track sources are in format MS-WAVE in a file with
|
||||||
|
suffix ".wav".
|
||||||
|
.TP
|
||||||
|
.BI \-tao
|
||||||
|
Write CD in Track At Once (TAO) mode, resp. sequential DVD-R[W] in Incremental
|
||||||
|
Streaming mode. This mode also applies pro-forma to overwriteable DVD media.
|
||||||
|
.br
|
||||||
|
Mode -tao can be used with track sources of unpredictable size, like standard
|
||||||
|
input or named pipes. It is also the only mode that can be used for writing
|
||||||
|
to appendable media which already hold data. With unformatted DVD-R[W] it is
|
||||||
|
the only mode which allows -multi.
|
||||||
|
.TP
|
||||||
|
.BI \-toc
|
||||||
|
Print the table of content (TOC) which describes the tracks recorded on disc.
|
||||||
|
The output contains all info from option -atip plus lines which begin with
|
||||||
|
"track:", the track number, the word "lba:" and a number which gives the
|
||||||
|
start address of the track. Addresses are counted in CD sectors which with
|
||||||
|
SAO or TAO data tracks hold 2048 bytes each.
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
Example. Retrieve an afio archive from track number 2:
|
||||||
|
.br
|
||||||
|
tracknumber=2
|
||||||
|
.br
|
||||||
|
lba=$(cdrskin dev=/dev/cdrom -toc 2>&1 | \\
|
||||||
|
.br
|
||||||
|
grep '^track:[ ]*[ 0-9][0-9]' | \\
|
||||||
|
.br
|
||||||
|
tail +"$tracknumber" | head -1 | \\
|
||||||
|
.br
|
||||||
|
awk '{ print $4}' )
|
||||||
|
.br
|
||||||
|
dd if=/dev/cdrom bs=2048 skip="$lba" | \\
|
||||||
|
.br
|
||||||
|
afio -t - | less
|
||||||
|
.RE
|
||||||
|
.TP
|
||||||
|
.BI tsize= size
|
||||||
|
Announces the exact size of the next track source. This is necessary with any
|
||||||
|
write mode other than -tao if the track source is not a regular disk file, but
|
||||||
|
e.g. "-" (standard input) or a named pipe.
|
||||||
|
About size specifiers, see option fs=.
|
||||||
|
.br
|
||||||
|
If the track source does not deliver the predicted amount of bytes, the
|
||||||
|
remainder of the track is padded with zeros. This is not considered an error.
|
||||||
|
If on the other hand the track source delivers more than the announced bytes
|
||||||
|
then the track on media gets truncated to the predicted size and cdrskin exits
|
||||||
|
with non-zero value.
|
||||||
|
.TP
|
||||||
|
.BI \-v
|
||||||
|
Increment verbose level by one. Startlevel is 0 with only few messages.
|
||||||
|
Level 1 prints progress report with long running operations and also causes
|
||||||
|
some extra lines to be put out with info retrieval options.
|
||||||
|
Level 2 additionally reports about option settings derived from arguments or
|
||||||
|
startup files. Level 3 is for debugging and useful mainly in conjunction with
|
||||||
|
somebody who had a look into the program sourcecode.
|
||||||
|
.PP
|
||||||
|
Alphabetical list of options which are genuine to cdrskin and intended for
|
||||||
|
normal use:
|
||||||
|
.TP
|
||||||
|
.BI \--allow_setuid
|
||||||
|
Disable the loud warning about insecure discrepance between login user and
|
||||||
|
effective user which indicates application of chmod u+s to the program binary.
|
||||||
|
One should not do this chmod u+s , but it is an old cdrecord tradition.
|
||||||
|
.TP
|
||||||
|
.BI \--any_track
|
||||||
|
Allow source_addresses to begin with "-" (plus further characters) or to
|
||||||
|
contain a "=" character.
|
||||||
|
By default such arguments are seen as misspelled options. It is nevertheless
|
||||||
|
not possible to use one of the options listed with --list_ignored_options.
|
||||||
|
.TP
|
||||||
|
.BI \--demand_a_drive
|
||||||
|
Exit with a nonzero value if no drive can be found during a bus scan.
|
||||||
|
.TP
|
||||||
|
.BI \--devices
|
||||||
|
List the device file addresses of all accessible CD drives. In order to get
|
||||||
|
listed, a drive has to offer rw-permission for the cdrskin user and it may
|
||||||
|
not be busy. The superuser should be able to see all idle drives listed and
|
||||||
|
busy drives reported as "SORRY" messages.
|
||||||
|
.br
|
||||||
|
Each available drive gets listed by a line containing the following fields:
|
||||||
|
.br
|
||||||
|
Number dev='Devicefile' rw-Permissions : 'Vendor' 'Model'
|
||||||
|
.br
|
||||||
|
Number and Devicefile can both be used with option dev=, but number is
|
||||||
|
volatile (numbering changes if drives become busy).
|
||||||
|
.TP
|
||||||
|
.BI fifo_start_at= size
|
||||||
|
Do not wait for full fifo but start burning as soon as the given number
|
||||||
|
of bytes is read. This option may be helpful to bring the average throughput
|
||||||
|
near to the maximum throughput of a drive. A large fs= and a small
|
||||||
|
fifo_start_at= combine a quick burn start and a large savings buffer to
|
||||||
|
compensate for temporary lack of source data. At the beginning of burning,
|
||||||
|
the software protection against buffer underun is as weak as the size of
|
||||||
|
fifo_start_at= . So it is best if the drive offers hardware protection which
|
||||||
|
is enabled automatically if not driveropts=noburnfree is given.
|
||||||
|
.TP
|
||||||
|
.BI \--list_ignored_options
|
||||||
|
List all ignored cdrecord options. The "-" options cannot be used as addresses
|
||||||
|
of track sources. No track source address may begin with a text equal to an
|
||||||
|
option which ends by "=". The list is ended by an empty line.
|
||||||
|
.TP
|
||||||
|
.BI \--no_rc
|
||||||
|
Only if used as first command line argument this option prevents reading and
|
||||||
|
interpretation of eventual startup files. See section FILES below.
|
||||||
|
.TP
|
||||||
|
.BI \--prodvd_cli_compatible
|
||||||
|
Activates behavior modifications with some DVD situations which bring cdrskin
|
||||||
|
nearer to the behavior of cdrecord-ProDVD:
|
||||||
|
.br
|
||||||
|
Option -multi with unsuitable media is not an error but simply has no effect.
|
||||||
|
.br
|
||||||
|
Options blank=fast and blank=all deformat overwriteable DVD-RW media.
|
||||||
|
.br
|
||||||
|
Option blank=fast does indeed minmal blanking with DVD-RW. This may yield media
|
||||||
|
which can only do DAO but not Incremental Streaming.
|
||||||
|
.TP
|
||||||
|
.BI \--single_track
|
||||||
|
Accept only the last argument of the command line as track source address.
|
||||||
|
.TP
|
||||||
|
.BI tao_to_sao_tsize= size
|
||||||
|
Set an exact fixed size for the next track to be in effect only if the track
|
||||||
|
source cannot deliver a size prediction and no tsize= was specified and an
|
||||||
|
exact track size prediction is demanded by the write mode.
|
||||||
|
.br
|
||||||
|
This was the fallback from bad old times when cdrskin was unable to burn
|
||||||
|
in mode -tao . It came back with minimally blanked DVD-RW which allow no
|
||||||
|
Incremental Streaming (-tao) resp. with explicitly selected write mode -sao
|
||||||
|
for best DVD-ROM compatibility.
|
||||||
|
.br
|
||||||
|
If the track source delivers less bytes than announced then the missing ones
|
||||||
|
will be filled with zeros.
|
||||||
|
.TP
|
||||||
|
.BI write_start_address= byte_offset
|
||||||
|
Set the address on media where to start writing the track. With DVD+RW or
|
||||||
|
DVD-RAM byte_offset must be aligned to 2 KB blocks, but better is 32 kB.
|
||||||
|
With DVD-RW 32 kB alignment is mandatory.
|
||||||
|
.br
|
||||||
|
Other media are not suitable for this option yet.
|
||||||
|
.PP
|
||||||
|
Alphabetical list of options which are only intended for very special
|
||||||
|
situations and not for normal use:
|
||||||
|
.TP
|
||||||
|
.BI \--abort_handler
|
||||||
|
Establish default signal handling not to leave a drive in busy state
|
||||||
|
but rather to shut it down and to wait until it has ended the final operations.
|
||||||
|
This option is only needed for revoking eventual --ignore_signals or
|
||||||
|
--no_abort_handler.
|
||||||
|
.TP
|
||||||
|
.BI dev_translation= <sep><from><sep><to>
|
||||||
|
Set drive address alias. This was necessary before cdrskin-0.2.4 to manually
|
||||||
|
translate cdrecord addresses into cdrskin addresses.
|
||||||
|
.br
|
||||||
|
<sep> is a single character which may not occur in the address string
|
||||||
|
<from>. <from> is an address as expected to be given by the user via option
|
||||||
|
dev=. <to> is the address to be used instead whenever <from> is given.
|
||||||
|
More than one translation instruction can be given in one cdrskin run.
|
||||||
|
.br
|
||||||
|
E.g.: dev_translation=+ATA:1,0,0+/dev/sg1 dev_translation=+ATA:1,1,0+/dev/sg2
|
||||||
|
.TP
|
||||||
|
.BI \--drive_abort_on_busy
|
||||||
|
Linux specific: Abort process if a busy drive is encountered.
|
||||||
|
.TP
|
||||||
|
.BI \--drive_blocking
|
||||||
|
Linux specific: Try to wait for a busy drive to become free.
|
||||||
|
This is not guaranteed to work with all drivers. Some need nonblocking i/o.
|
||||||
|
.TP
|
||||||
|
.BI \--drive_not_exclusive
|
||||||
|
Linux specific: Do not ask the operating system to prevent opening busy drives.
|
||||||
|
Wether this leads to senseful behavior depends on operating system and kernel.
|
||||||
|
.TP
|
||||||
|
.BI \--drive_scsi_exclusive
|
||||||
|
Linux specific:
|
||||||
|
Try to exclusively reserve device files /dev/srN, /dev/scdM, /dev/stK of drive.
|
||||||
|
this would be helpful to protect against collisions with program growisofs.
|
||||||
|
Regrettably on Linux kernel 2.4 with ide-scsi emulation this seems not to
|
||||||
|
work. Wether it becomes helpful with new Linux systems has to be evaluated.
|
||||||
|
.TP
|
||||||
|
.BI \--fifo_disable
|
||||||
|
Disable fifo despite any fs=.
|
||||||
|
.TP
|
||||||
|
.BI \--fifo_per_track
|
||||||
|
Use a separate fifo for each track.
|
||||||
|
.TP
|
||||||
|
.BI grab_drive_and_wait= seconds
|
||||||
|
Open the addressed drive, wait the given number of seconds, release the drive,
|
||||||
|
and do normal work as indicated by the other options used. This option helps
|
||||||
|
to explore the program behavior when faced with busy drives. Just start a
|
||||||
|
second cdrskin with option --devices while grab_drive_and_wait= is still
|
||||||
|
active.
|
||||||
|
.TP
|
||||||
|
.BI \--ignore_signals
|
||||||
|
Try to ignore any signals rather than to abort the program. This is not a
|
||||||
|
very good idea. You might end up waiting a very long time for cdrskin
|
||||||
|
to finish.
|
||||||
|
.TP
|
||||||
|
.BI \--no_abort_handler
|
||||||
|
On signals exit even if the drive is in busy state. This is not a very good
|
||||||
|
idea. You might end up with a stuck drive that refuses to hand out the media.
|
||||||
|
.TP
|
||||||
|
.BI \--no_blank_appendable
|
||||||
|
Refuse to blank appendable CD-RW or DVD-RW. This is a feature that was once
|
||||||
|
builtin with libburn. No information available for what use case it was needed.
|
||||||
|
.TP
|
||||||
|
.BI \--no_convert_fs_adr
|
||||||
|
Do only literal translations of dev=. This prevents cdrskin from test-opening
|
||||||
|
device files in order to find one that matches the given dev= specifier.
|
||||||
|
.br
|
||||||
|
Partly Linux specific:
|
||||||
|
Such opening is needed for Bus,Target,Lun addresses unless option
|
||||||
|
--old_pseudo_scsi_adr is given. It is also needed to resolve device file
|
||||||
|
addresses which are not listed with cdrskin --devices but nevertheless point
|
||||||
|
to a usable drive. (Like /dev/sr0 using the same SCSI address as /dev/sg0.)
|
||||||
|
.TP
|
||||||
|
.BI \--old_pseudo_scsi_adr
|
||||||
|
Linux specific:
|
||||||
|
Use and report literal Bus,Target,Lun addresses rather than real SCSI and
|
||||||
|
pseudo ATA addresses. This method is outdated and was never compatible with
|
||||||
|
original cdrecord.
|
||||||
|
.br
|
||||||
|
.SH EXAMPLES
|
||||||
|
.SS
|
||||||
|
.B Get an overview of drives and their addresses:
|
||||||
|
.br
|
||||||
|
cdrskin -scanbus
|
||||||
|
.br
|
||||||
|
cdrskin dev=ATA -scanbus
|
||||||
|
.br
|
||||||
|
cdrskin --devices
|
||||||
|
.SS
|
||||||
|
.B Get info about a particular drive or loaded media:
|
||||||
|
.br
|
||||||
|
cdrskin dev=0,1,0 -checkdrive
|
||||||
|
.br
|
||||||
|
cdrskin dev=ATA:1,0,0 -v -atip
|
||||||
|
.br
|
||||||
|
cdrskin dev=/dev/hdc -toc
|
||||||
|
.SS
|
||||||
|
.B Make used CD-RW or used unformatted DVD-RW writable again:
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=/dev/sg1 blank=fast -eject
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=/dev/dvd blank=all -eject
|
||||||
|
.SS
|
||||||
|
.B Format DVD-RW to avoid need for blanking before re-use:
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=/dev/sr0 blank=format_overwrite
|
||||||
|
.SS
|
||||||
|
.B De-format DVD-RW to make it capable of multi-session again:
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=/dev/sr0 blank=deformat_sequential
|
||||||
|
.SS
|
||||||
|
.B Write ISO-9660 filesystem image as only one to blank or formatted media:
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=/dev/hdc speed=12 fs=8m \\
|
||||||
|
.br
|
||||||
|
-sao -eject padsize=300k my_image.iso
|
||||||
|
.SS
|
||||||
|
.B Write compressed afio archive on-the-fly:
|
||||||
|
.br
|
||||||
|
find . | afio -oZ - | \\
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=0,1,0 fs=32m speed=8 \\
|
||||||
|
.br
|
||||||
|
-tao padsize=300k -
|
||||||
|
.SS
|
||||||
|
.B Write several sessions to the same CD or DVD-R[W]:
|
||||||
|
.br
|
||||||
|
cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 1.iso
|
||||||
|
.br
|
||||||
|
cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 2.iso
|
||||||
|
.br
|
||||||
|
cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 3.iso
|
||||||
|
.br
|
||||||
|
cdrskin dev=/dev/hdc -v padsize=300k -tao 4.iso
|
||||||
|
.SS
|
||||||
|
.B Get CD or DVD-R[W] multi-session info for option -C of program mkisofs:
|
||||||
|
.br
|
||||||
|
c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null)
|
||||||
|
.br
|
||||||
|
mkisofs ... -C "$c_values" ...
|
||||||
|
.SS
|
||||||
|
.B Write audio tracks to CD:
|
||||||
|
.br
|
||||||
|
cdrskin -v dev=ATA:1,0,0 speed=48 -sao \\
|
||||||
|
.br
|
||||||
|
track1.wav track2.au -audio -swab track3.raw
|
||||||
|
.br
|
||||||
|
.SH FILES
|
||||||
|
If not --no_rc is given as the first argument then cdrskin attempts on
|
||||||
|
startup to read the arguments from the following files:
|
||||||
|
.PP
|
||||||
|
.br
|
||||||
|
.B /etc/default/cdrskin
|
||||||
|
.br
|
||||||
|
.B /etc/opt/cdrskin/rc
|
||||||
|
.br
|
||||||
|
.B /etc/cdrskin/cdrskin.conf
|
||||||
|
.br
|
||||||
|
.B $HOME/.cdrskinrc
|
||||||
|
.br
|
||||||
|
.PP
|
||||||
|
The files are read in the sequence given above, but none of them is
|
||||||
|
required for cdrskin to function properly. Each readable line is treated
|
||||||
|
as one single argument. No extra blanks.
|
||||||
|
A first character '#' marks a comment, empty lines are ignored.
|
||||||
|
.SS
|
||||||
|
.B Example content of a startup file:
|
||||||
|
.br
|
||||||
|
# This is the default device
|
||||||
|
.br
|
||||||
|
dev=0,1,0
|
||||||
|
.br
|
||||||
|
# To accomodate to remnant cdrskin-0.2.2 addresses
|
||||||
|
.br
|
||||||
|
dev_translation=+1,0,0+0,1,0
|
||||||
|
.br
|
||||||
|
# Some more options
|
||||||
|
.br
|
||||||
|
fifo_start_at=0
|
||||||
|
.br
|
||||||
|
fs=16m
|
||||||
|
.br
|
||||||
|
.SH SEE ALSO
|
||||||
|
.TP
|
||||||
|
Formatting track sources for cdrskin:
|
||||||
|
.br
|
||||||
|
.BR mkisofs (8),
|
||||||
|
.BR genisoimage (8),
|
||||||
|
.BR afio (1),
|
||||||
|
.BR star (1)
|
||||||
|
.br
|
||||||
|
.TP
|
||||||
|
Other CD/DVD burn programs:
|
||||||
|
.br
|
||||||
|
.BR cdrecord (1),
|
||||||
|
.BR wodim (1)
|
||||||
|
.br
|
||||||
|
.TP
|
||||||
|
For DVD burning (also tutor of libburn's DVD capabilities):
|
||||||
|
.br
|
||||||
|
.BR growisofs (1)
|
||||||
|
.br
|
||||||
|
.SH AUTHOR
|
||||||
|
cdrskin was written by Thomas Schmitt <scdbackup@gmx.net>.
|
||||||
|
.PP
|
||||||
|
This manual page was written by George Danchev <danchev@spnet.net> and
|
||||||
|
Thomas Schmitt, for the Debian project and for all others.
|
||||||
|
|
2878
cdrskin/cdrskin.c
2878
cdrskin/cdrskin.c
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<HEAD>
|
<HEAD>
|
||||||
<META NAME="description" CONTENT="cdrskin, a limited cdrecord compatibility wrapper for libburn">
|
<META NAME="description" CONTENT="cdrskin, a limited cdrecord compatibility wrapper for libburn">
|
||||||
<META NAME="keywords" CONTENT="cdrskin, libburn, burn, CD, linux, CDR, CD-R, CDRW, CD-RW, cdrecord, compatible, scdbackup, burning">
|
<META NAME="keywords" CONTENT="cdrskin, libburn, libburnia, burn, CD, linux, CDR, CD-R, CDRW, CD-RW, cdrecord, compatible, scdbackup, burning">
|
||||||
<META NAME="robots" CONTENT="follow">
|
<META NAME="robots" CONTENT="follow">
|
||||||
<TITLE>cdrskin homepage english</TITLE>
|
<TITLE>cdrskin homepage english</TITLE>
|
||||||
</HEAD>
|
</HEAD>
|
||||||
@ -11,7 +11,12 @@
|
|||||||
<FONT SIZE=+1>
|
<FONT SIZE=+1>
|
||||||
|
|
||||||
<CENTER>
|
<CENTER>
|
||||||
<P><H2>Homepage of</H2><H1><BR>cdrskin</H1><BR>
|
<A HREF="http://en.wikipedia.org/wiki/D%C3%B6ner_kebab">
|
||||||
|
<IMG SRC="doener_150x200_tr_octx.gif" BORDER=0
|
||||||
|
ALT="cdrskin logo: Doener mit Scharf">
|
||||||
|
</A>
|
||||||
|
<P><H2> Homepage of </H2>
|
||||||
|
<H1> cdrskin </H1>
|
||||||
<!-- <FONT SIZE=+0><A HREF="cdrskin_ger.html">deutsch (german)</A></FONT> -->
|
<!-- <FONT SIZE=+0><A HREF="cdrskin_ger.html">deutsch (german)</A></FONT> -->
|
||||||
|
|
||||||
<H2>Limited cdrecord compatibility wrapper for libburn</H2>
|
<H2>Limited cdrecord compatibility wrapper for libburn</H2>
|
||||||
@ -20,7 +25,7 @@
|
|||||||
<P>
|
<P>
|
||||||
<H2>Purpose:</H2>
|
<H2>Purpose:</H2>
|
||||||
<UL>
|
<UL>
|
||||||
<LI>Burns preformatted data to CD-R or CD-RW</LI>
|
<LI>Burns preformatted data to CD-R, CD-RW, DVD-R, DVD-RW, DVD-RAM, DVD+RW</LI>
|
||||||
</UL>
|
</UL>
|
||||||
</P>
|
</P>
|
||||||
<P>
|
<P>
|
||||||
@ -29,9 +34,9 @@
|
|||||||
|
|
||||||
<P>
|
<P>
|
||||||
<H2>Hardware requirements:</H2>
|
<H2>Hardware requirements:</H2>
|
||||||
A CD recorder suitable for
|
A CD/DVD recorder suitable for
|
||||||
<A HREF="http://libburn.pykix.org">libburn.pykix.org</A>
|
<A HREF="http://libburnia.pykix.org">libburnia.pykix.org</A>
|
||||||
(SCSI or IDE/ATAPI writers compliant to mmc standard).
|
(SCSI or IDE/ATAPI writers compliant to standard MMC-3 or higher).
|
||||||
<BR>
|
<BR>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
@ -51,8 +56,8 @@ A CD recorder suitable for
|
|||||||
GPL software included:<BR>
|
GPL software included:<BR>
|
||||||
</H2>
|
</H2>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>libburn-0.2.1 (inofficially stable SVN snapshot)</DT>
|
<DT>libburn-0.3.2</DT>
|
||||||
<DD>(by Derek Foreman, Ben Jansens, and team of libburn.pykix.org)</DD>
|
<DD>(by Derek Foreman, Ben Jansens, and team of libburnia.pykix.org)</DD>
|
||||||
<DD>transfers data to CD</DD>
|
<DD>transfers data to CD</DD>
|
||||||
</DL>
|
</DL>
|
||||||
</P>
|
</P>
|
||||||
@ -80,29 +85,81 @@ Ports to other usable systems are appreciated. Reports are welcome.
|
|||||||
This has been achieved quite sufficiently for the needs of backup tool
|
This has been achieved quite sufficiently for the needs of backup tool
|
||||||
<A HREF="http://scdbackup.sourceforge.net/main_eng.html">scdbackup</A>
|
<A HREF="http://scdbackup.sourceforge.net/main_eng.html">scdbackup</A>
|
||||||
and for data CD projects of <A HREF="http://www.k3b.org">K3b</A>
|
and for data CD projects of <A HREF="http://www.k3b.org">K3b</A>
|
||||||
(see <A HREF="#examples">examples</A>).<BR>
|
(see <A HREF="#examples">examples</A>).
|
||||||
|
Suitability for audio CD frontends has been improved much and is now being
|
||||||
|
evaluated.<BR>
|
||||||
|
Most DVD types are written in pseudo -tao modes which are very different
|
||||||
|
from the write mode DAO used by cdrecord(-ProDVD). With DVD-R[W] cdrskin
|
||||||
|
can use this write mode, too.<BR>
|
||||||
Further enhancements depend on people who can describe and discuss their
|
Further enhancements depend on people who can describe and discuss their
|
||||||
wishes as well as on the development of libburn.</DT>
|
wishes as well as on the development of libburn.</DT>
|
||||||
<BR><BR>
|
<BR><BR>
|
||||||
<DT><A HREF="cdrskin_help">cdrskin -help</A></DT>
|
<DT>Get an overview of drives and their addresses:</DT>
|
||||||
<DD>reports the cdrecord compatible options</DD>
|
<DD>$<KBD> cdrskin -scanbus</KBD></DD>
|
||||||
<DT><A HREF="cdrskin__help">cdrskin --help</A></DT>
|
<DD>$<KBD> cdrskin dev=ATA -scanbus</KBD></DD>
|
||||||
<DD>reports the non-cdrecord options</DD>
|
<DD>$<KBD> cdrskin --devices</KBD></DD>
|
||||||
<DT><A HREF="http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html">man cdrecord</A></DT>
|
|
||||||
<DD>documents the standard for which cdrskin is striving.
|
<DT>Get info about a particular drive or loaded media:</DT>
|
||||||
<B>Do not bother Joerg Schilling with any cdrskin problems.</B>
|
<DD>$<KBD> cdrskin dev=0,1,0 -checkdrive</KBD></DD>
|
||||||
|
<DD>$<KBD> cdrskin dev=ATA:1,0,0 -v -atip</KBD></DD>
|
||||||
|
<DD>$<KBD> cdrskin dev=/dev/hdc -toc</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Make used CD-RW or used unformatted DVD-RW writable again:</DT>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=/dev/sg1 blank=fast -eject</KBD></DD>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=/dev/dvd blank=all -eject</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Format DVD-RW to avoid need for blanking before re-use:</DT>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=/dev/sr0 blank=format_overwrite</KBD></DD>
|
||||||
|
|
||||||
|
<DT>De-format DVD-RW to make it capable of multi-session again:</DT>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=/dev/sr0 blank=deformat_sequential</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Write ISO-9660 filesystem image as only one to blank or formatted media:
|
||||||
|
</DT>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=/dev/hdc speed=12 fs=8m \</KBD></DD>
|
||||||
|
<DD><KBD> -sao -eject padsize=300k my_image.iso</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Write compressed afio archive on-the-fly:</DT>
|
||||||
|
<DD>$<KBD> find . | afio -oZ - | \</KBD></DD>
|
||||||
|
<DD><KBD> cdrskin -v dev=0,1,0 fs=32m speed=8 \</KBD></DD>
|
||||||
|
<DD><KBD> -tao padsize=300k -</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Write several sessions to the same CD or DVD-R[W]:</DT>
|
||||||
|
<DD>$<KBD> cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 1.iso</KBD>
|
||||||
|
</DD>
|
||||||
|
<DD>$<KBD> cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 2.iso</KBD>
|
||||||
|
</DD>
|
||||||
|
<DD>$<KBD> cdrskin dev=/dev/hdc -v padsize=300k -multi -tao 3.iso</KBD>
|
||||||
|
</DD>
|
||||||
|
<DD>$<KBD> cdrskin dev=/dev/hdc -v padsize=300k -tao 4.iso</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Get CD or DVD-R[W] multi-session info for option -C of program mkisofs:</DT>
|
||||||
|
<DD>$<KBD> c_values=$(cdrskin dev=/dev/sr0 -msinfo 2>/dev/null)</KBD></DD>
|
||||||
|
<DD>$<KBD> mkisofs ... -C "$c_values" ...</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Write audio tracks to CD:</DT>
|
||||||
|
<DD>$<KBD> cdrskin -v dev=ATA:1,0,0 speed=48 -sao \</KBD></DD>
|
||||||
|
<DD><KBD> track1.wav track2.au -audio -swab track3.raw</KBD></DD>
|
||||||
|
|
||||||
|
<DT>Get overview of the cdrecord compatible options:</DT>
|
||||||
|
<DD>$<KBD> <A HREF="cdrskin_help">cdrskin -help</A></KBD></DD>
|
||||||
|
|
||||||
|
<DT>Get overview of the non-cdrecord options:</DT>
|
||||||
|
<DD>$<KBD> <A HREF="cdrskin__help">cdrskin --help</A></KBD></DD>
|
||||||
|
|
||||||
|
<DT>Read the detailed manual page:</DT>
|
||||||
|
<DD>$<KBD> <A HREF="man_1_cdrskin.html">man cdrskin</A></KBD></DD>
|
||||||
|
</DL>
|
||||||
|
|
||||||
|
<DL>
|
||||||
|
<DT>Read about the standard for which cdrskin is striving:</DT>
|
||||||
|
<DD>$<KBD>
|
||||||
|
<A HREF="http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html">
|
||||||
|
man cdrecord</A></KBD></DD>
|
||||||
|
<DD><B>Do not bother Joerg Schilling with any cdrskin problems.</B>
|
||||||
(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding
|
(Be cursed if you install cdrskin as "cdrecord" without clearly forwarding
|
||||||
this "don't bother Joerg" demand.)
|
this "don't bother Joerg" demand.)
|
||||||
</DD>
|
</DD>
|
||||||
<DT>Currently (and sparsely) supported gestures :</DT>
|
|
||||||
<DD>$ cdrskin -scanbus</DD>
|
|
||||||
<DD>$ cdrskin dev=1,1,0 -checkdrive</DD>
|
|
||||||
<DD>$ cdrskin dev=1,1,0 -atip</DD>
|
|
||||||
<DD>$ cdrskin -v dev=1,1,0 blank=all eject_device=/dev/cdrom -eject</DD>
|
|
||||||
<DD>$ cdrskin -v dev=1,1,0 blank=fast eject_device=/dev/cdrom -eject</DD>
|
|
||||||
<DD>$ cdrskin -v dev=1,1,0 speed=12 fs=8m -sao driveropts=burnfree eject_device=/dev/cdrom -eject padsize=300k my_image.iso</DD>
|
|
||||||
<DD>$ cdrskin -v dev=1,1,0 ... ... track_1.iso padsize=300k track_2.afio</DD>
|
|
||||||
<DD>$ find . | afio -oZ - | cdrskin -v dev=1,1,0 fs=32m speed=8 -sao driveropts=burnfree padsize=300k tsize=650m -</DD>
|
|
||||||
</DL>
|
</DL>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
@ -111,15 +168,7 @@ wishes as well as on the development of libburn.</DT>
|
|||||||
<UL>
|
<UL>
|
||||||
<DT></DT>
|
<DT></DT>
|
||||||
<LI>
|
<LI>
|
||||||
No audio features yet. (Note: Option -audio is enabled in cdrskin-0.1.5 as
|
Appending sessions to unclosed media is restricted to write mode TAO.
|
||||||
offered below, but no beheading of .au or .wav files takes place yet.)
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
Burns only a single closed session. No -multi option yet.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
No TAO mode and therefore no writing on-the-fly without a predefined
|
|
||||||
source size.
|
|
||||||
</LI>
|
</LI>
|
||||||
<LI>
|
<LI>
|
||||||
cdrskin -scanbus or --devices hangs for quite a while if there is
|
cdrskin -scanbus or --devices hangs for quite a while if there is
|
||||||
@ -133,12 +182,6 @@ In severe cases it might be necessary to guess the device name /dev/sgN resp.
|
|||||||
/dev/hdX of the non-ill burner if it cannot be found otherwise among its
|
/dev/hdX of the non-ill burner if it cannot be found otherwise among its
|
||||||
ill peers. Alternatively one can guess the address of the ill device, remove
|
ill peers. Alternatively one can guess the address of the ill device, remove
|
||||||
rw-permissions and retry the bus scan as non-superuser.
|
rw-permissions and retry the bus scan as non-superuser.
|
||||||
<LI>
|
|
||||||
Burners other than /dev/sg0 and /dev/hdX (i.e. without ide-scsi) need a user
|
|
||||||
supplied device address for program eject.
|
|
||||||
(Note: -eject is now working in libburn-0.2.1 SVN and the workaround described
|
|
||||||
here is obsolete with cdrskin-0.1.5 as offered below.)
|
|
||||||
</LI>
|
|
||||||
</UL>
|
</UL>
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
@ -147,39 +190,41 @@ here is obsolete with cdrskin-0.1.5 as offered below.)
|
|||||||
<P>
|
<P>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>Download as source code (see README):</DT>
|
<DT>Download as source code (see README):</DT>
|
||||||
<DD><A HREF="cdrskin-0.1.4.tar.gz">cdrskin-0.1.4.tar.gz</A>
|
<DD><A HREF="cdrskin-0.3.2.pl00.tar.gz">cdrskin-0.3.2.pl00.tar.gz</A>
|
||||||
(580 KB).
|
(570 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DD>
|
<DD>
|
||||||
The "stable" cdrskin tarballs are tested and eventually slightly modified
|
The "stable" cdrskin tarballs are source code identical with "stable"
|
||||||
SVN snapshots from libburn.pykix.org . All modifications are to be re-merged
|
libburn releases or with "stabilized" libburn SVN snapshots. They get
|
||||||
into that SVN repository.<BR>
|
produced via a different procedure, though.<BR>
|
||||||
cdrskin is part of libburn - full libburn is provided with cdrskin releases.
|
cdrskin is part of libburn - full libburn is provided with cdrskin releases.
|
||||||
</DD>
|
</DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DT>Download as single x86 binaries (untar and move to /usr/bin/cdrskin):</DT>
|
<DT>Download as single x86 binaries (untar and move to /usr/bin/cdrskin):</DT>
|
||||||
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0.tar.gz">
|
<DD><A HREF="cdrskin_0.3.2.pl00-x86-suse9_0.tar.gz">
|
||||||
cdrskin_0.1.4-x86-suse9_0.tar.gz</A>, (50 KB),
|
cdrskin_0.3.2.pl00-x86-suse9_0.tar.gz</A>, (80 KB),
|
||||||
<DL>
|
<DL>
|
||||||
<DD>runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
|
<DD>runs on SuSE 9.0 (2.4.21) , RIP-14.4 (2.6.14) ,
|
||||||
Gentoo (2.6.15 x86_64 Athlon).</DD>
|
Gentoo (2.6.15 x86_64 Athlon).</DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DD><A HREF="cdrskin_0.1.4-x86-suse9_0-static.tar.gz">
|
<DD><A HREF="cdrskin_0.3.2.pl00-x86-suse9_0-static.tar.gz">
|
||||||
cdrskin_0.1.4-x86-suse9_0-static.tar.gz</A>, (250 KB), -static compiled,
|
cdrskin_0.3.2.pl00-x86-suse9_0-static.tar.gz</A>, (285 KB), -static compiled,
|
||||||
<DL>
|
<DL>
|
||||||
<DD>runs on SuSE 7.2 (2.4.4), and on the systems above.</DD>
|
<DD>runs on SuSE 7.2 (2.4.4), and on the systems above.</DD>
|
||||||
</DL>
|
</DL>
|
||||||
</DD>
|
</DD>
|
||||||
<DD> </DD>
|
|
||||||
</DL>
|
</DL>
|
||||||
<DL><DT>Documentation:</DT>
|
<DL><DT>Documentation:</DT>
|
||||||
<DD><A HREF="README_cdrskin">README</A> a short introduction</DD>
|
<DD><A HREF="README_cdrskin">README</A> a short introduction</DD>
|
||||||
<DD><A HREF="cdrskin__help">cdrskin --help</A> non-cdrecord options</DD>
|
<DD><A HREF="cdrskin__help">cdrskin --help</A> non-cdrecord options</DD>
|
||||||
<DD><A HREF="cdrskin_help">cdrskin -help</A> cdrecord compatible options</DD>
|
<DD><A HREF="cdrskin_help">cdrskin -help</A> cdrecord compatible options</DD>
|
||||||
|
<DD><A HREF="man_1_cdrskin.html">man cdrskin</A> the manual page</DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL><DT>Contact:</DT>
|
<DL><DT>Contact:</DT>
|
||||||
<DD>Thomas Schmitt, <A HREF="mailto:scdbackup@gmx.net">scdbackup@gmx.net</A></DD>
|
<DD>Thomas Schmitt, <A HREF="mailto:scdbackup@gmx.net">scdbackup@gmx.net</A></DD>
|
||||||
|
<DD>libburn development mailing list,
|
||||||
|
<A HREF="mailto:libburn-hackers@pykix.org">libburn-hackers@pykix.org</A></DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL><DT>License:</DT>
|
<DL><DT>License:</DT>
|
||||||
<DD><A HREF="COPYING_cdrskin">GPL</A>, an <A HREF="http://www.opensource.org/">Open Source</A> approved license</DD>
|
<DD><A HREF="COPYING_cdrskin">GPL</A>, an <A HREF="http://www.opensource.org/">Open Source</A> approved license</DD>
|
||||||
@ -189,17 +234,37 @@ cdrskin_0.1.4-x86-suse9_0-static.tar.gz</A>, (250 KB), -static compiled,
|
|||||||
|
|
||||||
<HR>
|
<HR>
|
||||||
|
|
||||||
|
<P>
|
||||||
|
Enhancements towards previous stable version cdrskin-0.3.0:
|
||||||
|
<UL>
|
||||||
|
<LI>Burnfree enabled by default</LI>
|
||||||
|
<LI>Multi-session burning to DVD-R
|
||||||
|
and sequential (i.e. unformatted) DVD-RW</LI>
|
||||||
|
<LI>Option -toc with sequential DVD-R[W]</LI>
|
||||||
|
<LI>Options -msinfo and msifile= with appendable DVD-R[W]</LI>
|
||||||
|
<LI>Single session DAO write mode with DVD-R[W]</LI>
|
||||||
|
</UL>
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<HR>
|
||||||
|
|
||||||
<P>
|
<P>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>Development snapshot:</DT>
|
<DT><H3>Development snapshot, version 0.3.3 :</H3></DT>
|
||||||
|
<DD>Enhancements towards stable version 0.3.2:
|
||||||
|
<UL>
|
||||||
|
<LI>- none yet -</LI>
|
||||||
|
</UL>
|
||||||
|
</DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DD><A HREF="README_cdrskin_devel">README 0.1.5</A>
|
<DD><A HREF="README_cdrskin_devel">README 0.3.3</A>
|
||||||
<DD><A HREF="cdrskin__help_devel">cdrskin_0.1.5 --help</A></DD>
|
<DD><A HREF="cdrskin__help_devel">cdrskin_0.3.3 --help</A></DD>
|
||||||
<DD><A HREF="cdrskin_help_devel">cdrskin_0.1.5 -help</A></DD>
|
<DD><A HREF="cdrskin_help_devel">cdrskin_0.3.3 -help</A></DD>
|
||||||
|
<DD><A HREF="man_1_cdrskin_devel.html">man cdrskin (as of 0.3.3)</A></DD>
|
||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DD>Maintainers of cdrskin unstable packages please use SVN of
|
<DT>Maintainers of cdrskin unstable packages please use SVN of
|
||||||
<A HREF="http://libburn.pykix.org"> libburn.pykix.org</A></DD>
|
<A HREF="http://libburnia.pykix.org"> libburnia.pykix.org</A></DT>
|
||||||
<DD>Download: <KBD><B>svn co http://libburn-svn.pykix.org/trunk libburn_pykix</B>
|
<DD>Download: <KBD><B>svn co http://libburnia-svn.pykix.org/libburn/trunk libburn_pykix</B>
|
||||||
</KBD></DD>
|
</KBD></DD>
|
||||||
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
<DD>Build: <KBD><B>cd libburn_pykix ; ./bootstrap ; ./configure ; make</B>
|
||||||
</KBD></DD>
|
</KBD></DD>
|
||||||
@ -211,19 +276,19 @@ vanilla tools like make and gcc are needed.</DD>
|
|||||||
<DD> </DD>
|
<DD> </DD>
|
||||||
<DT>The following downloads are intended for adventurous end users or
|
<DT>The following downloads are intended for adventurous end users or
|
||||||
admins with full system souvereignty.</DT>
|
admins with full system souvereignty.</DT>
|
||||||
<DT>Source (./bootstrap is already applied, build tested, for more see above
|
<DD>Source (./bootstrap is already applied, build tested, for more see
|
||||||
<A HREF="README_cdrskin_devel">upcoming README</A> ):
|
<A HREF="README_cdrskin_devel">upcoming README</A> ):
|
||||||
</DT>
|
</DD>
|
||||||
<DD>
|
<DD>
|
||||||
<A HREF="cdrskin-0.1.5.tar.gz">cdrskin-0.1.5.tar.gz</A>
|
<A HREF="cdrskin-0.3.3.tar.gz">cdrskin-0.3.3.tar.gz</A>
|
||||||
(580 KB).
|
(570 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DT>Binary (untar and move to /usr/bin/cdrskin):</DT>
|
<DD>Binary (untar and move to /usr/bin/cdrskin):</DD>
|
||||||
<DD><A HREF="cdrskin_0.1.5-x86-suse9_0.tar.gz">
|
<DD><A HREF="cdrskin_0.3.3-x86-suse9_0.tar.gz">
|
||||||
cdrskin_0.1.5-x86-suse9_0.tar.gz</A>, (50 KB).
|
cdrskin_0.3.3-x86-suse9_0.tar.gz</A>, (80 KB).
|
||||||
</DD>
|
</DD>
|
||||||
<DD><A HREF="cdrskin_0.1.5-x86-suse9_0-static.tar.gz">
|
<DD><A HREF="cdrskin_0.3.3-x86-suse9_0-static.tar.gz">
|
||||||
cdrskin_0.1.5-x86-suse9_0-static.tar.gz</A>, (250 KB)
|
cdrskin_0.3.3-x86-suse9_0-static.tar.gz</A>, (280 KB)
|
||||||
</DD>
|
</DD>
|
||||||
</DL>
|
</DL>
|
||||||
</P>
|
</P>
|
||||||
@ -239,6 +304,10 @@ Historic versions based on Derek's and Ben's
|
|||||||
<A HREF="http://icculus.org/burn">icculus.org/burn</A> :<BR>
|
<A HREF="http://icculus.org/burn">icculus.org/burn</A> :<BR>
|
||||||
<A HREF="cdrskin-0.1.2.0.2.ts.tar.gz">cdrskin-0.1.2.0.2.ts.tar.gz</A><BR>
|
<A HREF="cdrskin-0.1.2.0.2.ts.tar.gz">cdrskin-0.1.2.0.2.ts.tar.gz</A><BR>
|
||||||
<A HREF="cdrskin-0.1.3.0.2.ts.tar.gz">cdrskin-0.1.3.0.2.ts.tar.gz</A>
|
<A HREF="cdrskin-0.1.3.0.2.ts.tar.gz">cdrskin-0.1.3.0.2.ts.tar.gz</A>
|
||||||
|
<BR>
|
||||||
|
Very special thanks to Andy Polyakov whose
|
||||||
|
<A HREF="http://fy.chalmers.se/~appro/linux/DVD+RW/tools">dvd+rw-tools</A>
|
||||||
|
provide libburn with invaluable examples on how to deal with DVD media.
|
||||||
</P>
|
</P>
|
||||||
|
|
||||||
<HR>
|
<HR>
|
||||||
@ -276,25 +345,21 @@ is a GUI frontend which uses cdrecord for CD burning.)
|
|||||||
<DT>Example for a test session with a cdrecord based scdbackup installation:</DT>
|
<DT>Example for a test session with a cdrecord based scdbackup installation:</DT>
|
||||||
<DD>$ <KBD><B>cdrskin -scanbus</B></KBD></DD>
|
<DD>$ <KBD><B>cdrskin -scanbus</B></KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>scsibus1:</KBD></DD>
|
<DD><KBD> 2,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
<DD>$ <KBD><B>cdrskin -scanbus dev=ATA</B></KBD></DD>
|
||||||
<DD><KBD>scsibus2:</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD> 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD>$ <KBD><B>export SCDBACKUP_SCSI_ADR="2,2,0"</B></KBD></DD>
|
<DD>$ <KBD><B>export SCDBACKUP_SCSI_ADR="ATA:1,0,0"</B></KBD></DD>
|
||||||
<DD>$ <KBD><B>export SCDBACKUP_CDRECORD="cdrskin -v -v tao_to_sao_tsize=650m eject_device=/dev/cdrecorder"</B></KBD></DD>
|
<DD>$ <KBD><B>export SCDBACKUP_CDRECORD="cdrskin -v -v"</B></KBD></DD>
|
||||||
<DD>(eject_device= has to be the appropriate address for program eject)</DD>
|
|
||||||
<DD>$ <KBD><B>scdbackup_home</B></KBD></DD>
|
<DD>$ <KBD><B>scdbackup_home</B></KBD></DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>Example for a permanent configuration of cdrskin based scdbackup</DT>
|
<DT>Example for a permanent configuration of cdrskin based scdbackup</DT>
|
||||||
<DD>$ <KBD><B>cd scdbackup-0.8.6/inst</B></KBD></DD>
|
<DD>$ <KBD><B>cd scdbackup-0.8.6/inst</B></KBD></DD>
|
||||||
<DD>$ <KBD><B>export SCDBACKUP_USE_CDRSKIN=1</B></KBD></DD>
|
<DD>$ <KBD><B>export SCDBACKUP_USE_CDRSKIN=1</B></KBD></DD>
|
||||||
<DD>$ <KBD><B>export SCDBACKUP_EJECT_ADR=/dev/cdrecorder</B></KBD></DD>
|
|
||||||
<DD>$ <KBD><B>./CONFIGURE_CD</B></KBD></DD>
|
<DD>$ <KBD><B>./CONFIGURE_CD</B></KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>cdrskin 0.1.2 : limited cdrecord compatibility wrapper for libburn</KBD></DD>
|
<DD><KBD>cdrskin 0.3.2 : limited cdrecord compatibility wrapper for libburn</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
|
||||||
<DD><KBD> ------------------- SCSI devices. To be used like 0,0,0</KBD></DD>
|
|
||||||
</DL>
|
</DL>
|
||||||
If your system is stricken with some ill CD device then this can stall
|
If your system is stricken with some ill CD device then this can stall
|
||||||
and you will have to press <KBD>Ctrl+C</KBD> to abort.
|
and you will have to press <KBD>Ctrl+C</KBD> to abort.
|
||||||
@ -303,16 +368,18 @@ In this case, you may execute
|
|||||||
and try again.
|
and try again.
|
||||||
<DL>
|
<DL>
|
||||||
<DT></DT>
|
<DT></DT>
|
||||||
<DD><KBD> 1,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
<DD><KBD> ------------------- SCSI devices. To be used like 0,0,0</KBD></DD>
|
||||||
<DD><KBD> 2,2,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
<DD><KBD> 2,0,0 0) 'TEAC' 'CD-ROM CD-532S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD><KBD>------------------- end of SCSI device list</KBD></DD>
|
<DD><KBD> ------------------- end of SCSI device list</KBD></DD>
|
||||||
|
<DD><KBD> ------------------- ATA devices. To be used like ATA:0,0,0
|
||||||
|
<DD><KBD> 1,0,0 1) 'LITE-ON' 'LTR-48125S' '?' Removable CD-ROM</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD> * Your cdrecord offers -driveropts=burnfree with your recorder.</KBD></DD>
|
<DD><KBD> * Your cdrecord offers -driveropts=burnfree with your recorder.</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>scdbackup for CD 0.8.6 : First stage of installation done.</KBD></DD>
|
<DD><KBD>scdbackup for CD 0.8.6 : First stage of installation done.</KBD></DD>
|
||||||
<DD><KBD>...</KBD></DD>
|
<DD><KBD>...</KBD></DD>
|
||||||
<DD><KBD>Now give it a try. Run : scdbackup_home</KBD></DD>
|
<DD><KBD>Now give it a try. Run : scdbackup_home</KBD></DD>
|
||||||
<DD>$ <KBD><B>unset SCDBACKUP_USE_CDRSKIN SCDBACKUP_EJECT_ADR</B></KBD></DD>
|
<DD>$ <KBD><B>unset SCDBACKUP_USE_CDRSKIN</B></KBD></DD>
|
||||||
</DL>
|
</DL>
|
||||||
<DL>
|
<DL>
|
||||||
<DT>To get back to using cdrecord :</DT>
|
<DT>To get back to using cdrecord :</DT>
|
||||||
@ -350,10 +417,11 @@ the gestures necessary for their cdrecord applications.
|
|||||||
Contact me. Let's see what we can achieve.
|
Contact me. Let's see what we can achieve.
|
||||||
<BR>
|
<BR>
|
||||||
<BR>
|
<BR>
|
||||||
I am aware that libburn and cdrskin still have way to go until you can simply
|
libburn and cdrskin are now mature enough to substitute cdrecord in its
|
||||||
install cdrskin as cdrecord and may expect any application to run with it.
|
major use cases of CD burning. It is possible to foist cdrskin on various
|
||||||
Currently i do not encourage this approach, but of course such a replacement
|
software packages if it gets falsely named "cdrecord".
|
||||||
opportunity is the long term goal of a cdrecord compatibility wrapper.
|
I do not encourage this approach, but of course such a replacement
|
||||||
|
opportunity is the goal of a cdrecord compatibility wrapper.
|
||||||
<BR>
|
<BR>
|
||||||
<BR>
|
<BR>
|
||||||
It is very important to me that this project is not perceived as hostile
|
It is very important to me that this project is not perceived as hostile
|
||||||
@ -367,6 +435,10 @@ I owe him much. For cdrecord, for mkisofs, for star. Chapeau.
|
|||||||
<!-- <A NAME="bottom" HREF="main_ger.html#bottom">deutsch (german)</A>
|
<!-- <A NAME="bottom" HREF="main_ger.html#bottom">deutsch (german)</A>
|
||||||
<BR><BR>
|
<BR><BR>
|
||||||
-->
|
-->
|
||||||
|
<A HREF="http://en.wikipedia.org/wiki/D%C3%B6ner_kebab">
|
||||||
|
<IMG SRC="doener_150x200_tr.gif" BORDER=0
|
||||||
|
ALT="cdrskin logo: Doener mit Scharf"></A>
|
||||||
|
<BR><BR>
|
||||||
<FONT SIZE=+0>Enjoying free Open Source hosting by <A HREF="http://www.webframe.org">www.webframe.org</A><BR>
|
<FONT SIZE=+0>Enjoying free Open Source hosting by <A HREF="http://www.webframe.org">www.webframe.org</A><BR>
|
||||||
<A HREF="http://www.webframe.org">
|
<A HREF="http://www.webframe.org">
|
||||||
<IMG SRC="msfree.gif" ALT="100 % Microsoft free" BORDER=0></A><BR>
|
<IMG SRC="msfree.gif" ALT="100 % Microsoft free" BORDER=0></A><BR>
|
||||||
|
@ -1 +1 @@
|
|||||||
#define Cdrskin_timestamP "2006.09.20.134219"
|
#define Cdrskin_timestamP "2007.02.10.120001"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
A signal handler which cleans up an application and exits.
|
A signal handler which cleans up an application and exits.
|
||||||
|
|
||||||
Provided under GPL license within cdrskin and under BSD license elsewise.
|
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -23,8 +23,27 @@ typedef void (*sighandler_t)(int);
|
|||||||
|
|
||||||
#include "cleanup.h"
|
#include "cleanup.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef Cleanup_has_no_libburn_os_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "../libburn/os.h"
|
||||||
|
|
||||||
|
/* see os.h for name of particular os-*.h where this is defined */
|
||||||
|
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
|
||||||
|
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
|
||||||
|
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
|
||||||
|
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
|
||||||
|
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
|
||||||
|
|
||||||
|
|
||||||
|
#else /* ! Cleanup_has_no_libburn_os_H */
|
||||||
|
|
||||||
|
|
||||||
|
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
|
||||||
|
|
||||||
/* Signals to be caught */
|
/* Signals to be caught */
|
||||||
static int signal_list[]= {
|
static int signal_list[]= {
|
||||||
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||||
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||||
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||||
@ -32,7 +51,7 @@ static int signal_list[]= {
|
|||||||
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
||||||
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||||
};
|
};
|
||||||
static char *signal_name_list[]= {
|
static char *signal_name_list[]= {
|
||||||
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||||
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||||
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||||
@ -44,14 +63,19 @@ static int signal_list_count= 24;
|
|||||||
|
|
||||||
/* Signals not to be caught */
|
/* Signals not to be caught */
|
||||||
static int non_signal_list[]= {
|
static int non_signal_list[]= {
|
||||||
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, -1
|
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1
|
||||||
};
|
};
|
||||||
static int non_signal_list_count= 4;
|
static int non_signal_list_count= 5;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* Cleanup_has_no_libburn_os_H */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* run time dynamic part */
|
/* run time dynamic part */
|
||||||
static char cleanup_msg[4096]= {""};
|
static char cleanup_msg[4096]= {""};
|
||||||
static int cleanup_exiting= 0;
|
static int cleanup_exiting= 0;
|
||||||
|
static int cleanup_has_reported= -1234567890;
|
||||||
|
|
||||||
static void *cleanup_app_handle= NULL;
|
static void *cleanup_app_handle= NULL;
|
||||||
static Cleanup_app_handler_T cleanup_app_handler= NULL;
|
static Cleanup_app_handler_T cleanup_app_handler= NULL;
|
||||||
@ -62,26 +86,29 @@ static int Cleanup_handler_exit(int exit_value, int signum, int flag)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) {
|
||||||
|
fprintf(stderr,"\n%s\n",cleanup_msg);
|
||||||
|
cleanup_has_reported= signum;
|
||||||
|
}
|
||||||
if(cleanup_perform_app_handler_first)
|
if(cleanup_perform_app_handler_first)
|
||||||
if(cleanup_app_handler!=NULL) {
|
if(cleanup_app_handler!=NULL) {
|
||||||
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
if(ret==2)
|
if(ret==2 || ret==-2)
|
||||||
return(2);
|
return(2);
|
||||||
}
|
}
|
||||||
if(cleanup_exiting) {
|
if(cleanup_exiting) {
|
||||||
if(cleanup_msg[0]!=0)
|
|
||||||
fprintf(stderr,"%s\n",cleanup_msg);
|
|
||||||
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
||||||
getpid(),signum);
|
getpid(),signum);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
cleanup_exiting= 1;
|
cleanup_exiting= 1;
|
||||||
if(cleanup_msg[0]!=0)
|
|
||||||
fprintf(stderr,"%s\n",cleanup_msg);
|
|
||||||
alarm(0);
|
alarm(0);
|
||||||
if(!cleanup_perform_app_handler_first)
|
if(!cleanup_perform_app_handler_first)
|
||||||
if(cleanup_app_handler!=NULL)
|
if(cleanup_app_handler!=NULL) {
|
||||||
(*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
|
if(ret==2 || ret==-2)
|
||||||
|
return(2);
|
||||||
|
}
|
||||||
exit(exit_value);
|
exit(exit_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +142,12 @@ int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag)
|
|||||||
cleanup_msg[0]= 0;
|
cleanup_msg[0]= 0;
|
||||||
cleanup_app_handle= handle;
|
cleanup_app_handle= handle;
|
||||||
cleanup_app_handler= handler;
|
cleanup_app_handler= handler;
|
||||||
|
|
||||||
|
/* <<< make cleanup_exiting thread safe to get rid of this */
|
||||||
if(flag&4)
|
if(flag&4)
|
||||||
cleanup_perform_app_handler_first= 1;
|
cleanup_perform_app_handler_first= 1;
|
||||||
|
|
||||||
|
|
||||||
if(flag&1)
|
if(flag&1)
|
||||||
sig_handler= SIG_DFL;
|
sig_handler= SIG_DFL;
|
||||||
else if(flag&2)
|
else if(flag&2)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
A signal handler which cleans up an application and exits.
|
A signal handler which cleans up an application and exits.
|
||||||
|
|
||||||
Provided under GPL license within cdrskin and under BSD license elsewise.
|
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef Cleanup_includeD
|
#ifndef Cleanup_includeD
|
||||||
@ -13,7 +13,7 @@
|
|||||||
/** Layout of an application provided cleanup function using an application
|
/** Layout of an application provided cleanup function using an application
|
||||||
provided handle as first argument and the signal number as second
|
provided handle as first argument and the signal number as second
|
||||||
argument. The third argument is a flag bit field with no defined bits yet.
|
argument. The third argument is a flag bit field with no defined bits yet.
|
||||||
If the handler returns 2 then it has delegated exit() to some other
|
If the handler returns 2 or -2 then it has delegated exit() to some other
|
||||||
instance and the Cleanup handler shall return rather than exit.
|
instance and the Cleanup handler shall return rather than exit.
|
||||||
*/
|
*/
|
||||||
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
||||||
|
@ -6,37 +6,55 @@
|
|||||||
|
|
||||||
debug_opts=
|
debug_opts=
|
||||||
def_opts=
|
def_opts=
|
||||||
libvers="-DCdrskin_libburn_0_2_2"
|
largefile_opts="-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1"
|
||||||
|
libvers="-DCdrskin_libburn_0_3_2"
|
||||||
|
cleanup_src_or_obj="libburn/cleanup.o"
|
||||||
|
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||||
|
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||||
do_strip=0
|
do_strip=0
|
||||||
static_opts=
|
static_opts=
|
||||||
warn_opts="-Wall"
|
warn_opts="-Wall"
|
||||||
fifo_source="cdrskin/cdrfifo.c"
|
fifo_source="cdrskin/cdrfifo.c"
|
||||||
compile_cdrskin=1
|
compile_cdrskin=1
|
||||||
compile_cdrfifo=0
|
compile_cdrfifo=0
|
||||||
|
compile_dewav=0
|
||||||
|
|
||||||
for i in "$@"
|
for i in "$@"
|
||||||
do
|
do
|
||||||
if test "$i" = "-compile_cdrfifo"
|
if test "$i" = "-compile_cdrfifo"
|
||||||
then
|
then
|
||||||
compile_cdrfifo=1
|
compile_cdrfifo=1
|
||||||
elif test "$i" = "-tarball_0_2"
|
elif test "$i" = "-compile_dewav"
|
||||||
then
|
then
|
||||||
libvers=
|
compile_dewav=1
|
||||||
elif test "$i" = "-cvs_A51208"
|
|
||||||
then
|
|
||||||
libvers="-DCdrskin_libburn_cvs_A51208_tS"
|
|
||||||
elif test "$i" = "-cvs_A60220"
|
elif test "$i" = "-cvs_A60220"
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
libvers="-DCdrskin_libburn_cvs_A60220_tS"
|
||||||
elif test "$i" = "-libburn_0_2_1"
|
libdax_audioxtr_o=
|
||||||
|
libdax_msgs_o="libburn/message.o"
|
||||||
|
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
|
||||||
|
elif test "$i" = "-libburn_0_3_2"
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_0_2_1"
|
libvers="-DCdrskin_libburn_0_3_2"
|
||||||
elif test "$i" = "-libburn_0_2_2"
|
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||||
|
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||||
|
cleanup_src_or_obj="libburn/cleanup.o"
|
||||||
|
elif test "$i" = "-libburn_svn"
|
||||||
then
|
then
|
||||||
libvers="-DCdrskin_libburn_0_2_2"
|
libvers="-DCdrskin_libburn_0_3_3"
|
||||||
|
libdax_audioxtr_o="libburn/libdax_audioxtr.o"
|
||||||
|
libdax_msgs_o="libburn/libdax_msgs.o"
|
||||||
|
cleanup_src_or_obj="libburn/cleanup.o"
|
||||||
elif test "$i" = "-newapi" -o "$i" = "-experimental"
|
elif test "$i" = "-newapi" -o "$i" = "-experimental"
|
||||||
then
|
then
|
||||||
def_opts="$def_opts -DCdrskin_new_api_tesT"
|
def_opts="$def_opts -DCdrskin_new_api_tesT"
|
||||||
|
elif test "$i" = "-oldfashioned"
|
||||||
|
then
|
||||||
|
def_opts="$def_opts -DCdrskin_oldfashioned_api_usE"
|
||||||
|
cleanup_src_or_obj="-DCleanup_has_no_libburn_os_H cdrskin/cleanup.c"
|
||||||
|
elif test "$i" = "-no_largefile"
|
||||||
|
then
|
||||||
|
largefile_opts=
|
||||||
elif test "$i" = "-do_not_compile_cdrskin"
|
elif test "$i" = "-do_not_compile_cdrskin"
|
||||||
then
|
then
|
||||||
compile_cdrskin=0
|
compile_cdrskin=0
|
||||||
@ -56,16 +74,17 @@ do
|
|||||||
debug_opts="$debug_opts -O2"
|
debug_opts="$debug_opts -O2"
|
||||||
elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
|
elif test "$i" = "-help" -o "$i" = "--help" -o "$i" = "-h"
|
||||||
then
|
then
|
||||||
echo "cdrskin/compile_cdrskin.sh : to be executed within ./cdrskin-0.1.3.0.2.ts"
|
echo "cdrskin/compile_cdrskin.sh : to be executed within top level directory"
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
|
echo " -compile_cdrfifo compile program cdrskin/cdrfifo."
|
||||||
echo " -tarball_0_2 set macro to match libburn-0.2.ts.tar.gz"
|
echo " -compile_dewav compile program test/dewav without libburn."
|
||||||
echo " -cvs_A51208 set macro to match libburn-CVS of 8 Dec 2005."
|
|
||||||
echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006."
|
echo " -cvs_A60220 set macro to match libburn-CVS of 20 Feb 2006."
|
||||||
echo " -libburn_0_2_1 set macro to match libburn-SVN of 15 Sep 2006."
|
echo " -libburn_0_3_2 set macro to match libburn-0.3.2."
|
||||||
echo " -libburn_0_2_2 set macro to match libburn-0.2.2"
|
echo " -libburn_svn set macro to match current libburn-SVN."
|
||||||
|
echo " -no_largefile do not use 64 bit off_t (must match libburn)."
|
||||||
echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
|
echo " -do_not_compile_cdrskin omit compilation of cdrskin/cdrskin."
|
||||||
echo " -experimental use newly introduced libburn features."
|
echo " -experimental use newly introduced libburn features."
|
||||||
|
echo " -oldfashioned use pre-0.2.2 libburn features only."
|
||||||
echo " -do_diet produce capability reduced lean version."
|
echo " -do_diet produce capability reduced lean version."
|
||||||
echo " -do_strip apply program strip to compiled programs."
|
echo " -do_strip apply program strip to compiled programs."
|
||||||
echo " -g compile with cc option -g."
|
echo " -g compile with cc option -g."
|
||||||
@ -85,15 +104,23 @@ echo "Build timestamp : $timestamp"
|
|||||||
|
|
||||||
if test "$compile_cdrskin"
|
if test "$compile_cdrskin"
|
||||||
then
|
then
|
||||||
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts"
|
echo "compiling program cdrskin/cdrskin.c $static_opts $debug_opts $libvers $def_opts $cleanup_src_or_obj"
|
||||||
cc $warn_opts -I. $static_opts $debug_opts $libvers $def_opts \
|
cc -I. \
|
||||||
|
$warn_opts \
|
||||||
|
$static_opts \
|
||||||
|
$debug_opts \
|
||||||
|
$libvers \
|
||||||
|
$largefile_opts \
|
||||||
|
$def_opts \
|
||||||
|
\
|
||||||
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
-DCdrskin_build_timestamP='"'"$timestamp"'"' \
|
||||||
\
|
\
|
||||||
-o cdrskin/cdrskin \
|
-o cdrskin/cdrskin \
|
||||||
\
|
\
|
||||||
cdrskin/cdrskin.c \
|
cdrskin/cdrskin.c \
|
||||||
$fifo_source \
|
$fifo_source \
|
||||||
cdrskin/cleanup.c \
|
\
|
||||||
|
$cleanup_src_or_obj \
|
||||||
\
|
\
|
||||||
libburn/async.o \
|
libburn/async.o \
|
||||||
libburn/debug.o \
|
libburn/debug.o \
|
||||||
@ -104,9 +131,10 @@ then
|
|||||||
libburn/source.o \
|
libburn/source.o \
|
||||||
libburn/structure.o \
|
libburn/structure.o \
|
||||||
\
|
\
|
||||||
libburn/message.o \
|
|
||||||
libburn/sg.o \
|
libburn/sg.o \
|
||||||
libburn/write.o \
|
libburn/write.o \
|
||||||
|
$libdax_audioxtr_o \
|
||||||
|
$libdax_msgs_o \
|
||||||
\
|
\
|
||||||
libburn/mmc.o \
|
libburn/mmc.o \
|
||||||
libburn/sbc.o \
|
libburn/sbc.o \
|
||||||
@ -120,6 +148,17 @@ then
|
|||||||
libburn/lec.o \
|
libburn/lec.o \
|
||||||
\
|
\
|
||||||
-lpthread
|
-lpthread
|
||||||
|
|
||||||
|
ret=$?
|
||||||
|
if test "$ret" = 0
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo >&2
|
||||||
|
echo "+++ FATAL : Compilation of cdrskin failed" >&2
|
||||||
|
echo >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$compile_cdrfifo" = 1
|
if test "$compile_cdrfifo" = 1
|
||||||
@ -129,8 +168,43 @@ then
|
|||||||
-DCdrfifo_standalonE \
|
-DCdrfifo_standalonE \
|
||||||
-o cdrskin/cdrfifo \
|
-o cdrskin/cdrfifo \
|
||||||
cdrskin/cdrfifo.c
|
cdrskin/cdrfifo.c
|
||||||
|
|
||||||
|
ret=$?
|
||||||
|
if test "$ret" = 0
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo >&2
|
||||||
|
echo "+++ FATAL : Compilation of cdrfifo failed" >&2
|
||||||
|
echo >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$compile_dewav" = 1
|
||||||
|
then
|
||||||
|
echo "compiling program test/dewav.c -DDewav_without_libburN $static_opts $debug_opts"
|
||||||
|
cc $static_opts $debug_opts \
|
||||||
|
-DDewav_without_libburN \
|
||||||
|
-o test/dewav \
|
||||||
|
test/dewav.c \
|
||||||
|
libburn/libdax_audioxtr.o \
|
||||||
|
libburn/libdax_msgs.o \
|
||||||
|
\
|
||||||
|
-lpthread
|
||||||
|
|
||||||
|
ret=$?
|
||||||
|
if test "$ret" = 0
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo >&2
|
||||||
|
echo "+++ FATAL : Compilation of test/dewav failed" >&2
|
||||||
|
echo >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if test "$do_strip" = 1
|
if test "$do_strip" = 1
|
||||||
then
|
then
|
||||||
echo "stripping result cdrskin/cdrskin"
|
echo "stripping result cdrskin/cdrskin"
|
||||||
|
73
cdrskin/convert_man_to_html.sh
Executable file
73
cdrskin/convert_man_to_html.sh
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# convert_man_to_html.sh - ts A61214
|
||||||
|
#
|
||||||
|
# Generates a HTML version of man page cdrskin.1
|
||||||
|
#
|
||||||
|
# To be executed within the libburn toplevel directory (like ./libburn-0.2.7)
|
||||||
|
#
|
||||||
|
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
man_dir=$(pwd)"/cdrskin"
|
||||||
|
export MANPATH="$man_dir"
|
||||||
|
manpage="cdrskin"
|
||||||
|
raw_html=$(pwd)/"cdrskin/raw_man_1_cdrskin.html"
|
||||||
|
htmlpage=$(pwd)/"cdrskin/man_1_cdrskin.html"
|
||||||
|
|
||||||
|
if test -r "$manpage"
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
echo "Cannot find readable man page source $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -e "$man_dir"/man1
|
||||||
|
then
|
||||||
|
dummy=dummy
|
||||||
|
else
|
||||||
|
ln -s . "$man_dir"/man1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$1" = "-work_as_filter"
|
||||||
|
then
|
||||||
|
|
||||||
|
# set -x
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e 's/<meta name="generator" content="groff -Thtml, see www.gnu.org">/<meta name="generator" content="groff -Thtml, via man -H, via cdrskin\/convert_man_to_html.sh">/' \
|
||||||
|
-e 's/<meta name="Content-Style" content="text\/css">/<meta name="Content-Style" content="text\/css"><META NAME="description" CONTENT="man page of cdrskin"><META NAME="keywords" CONTENT="man cdrskin, manual, cdrskin, CD, CD-RW, CD-R, burning, cdrecord, compatible"><META NAME="robots" CONTENT="follow">/' \
|
||||||
|
-e 's/<title>CDRSKIN<\/title>/<title>man 1 cdrskin<\/title>/' \
|
||||||
|
-e 's/<h1 align=center>CDRSKIN<\/h1>/<h1 align=center>man 1 cdrskin<\/h1>/' \
|
||||||
|
-e 's/<body>/<body BGCOLOR="#F5DEB3" TEXT=#000000 LINK=#0000A0 VLINK=#800000>/' \
|
||||||
|
-e 's/<b>Overview of features:<\/b>/\ <BR><b>Overview of features:<\/b>/' \
|
||||||
|
-e 's/<b>Track recording model:<\/b>/\ <BR><b>Track recording model:<\/b>/' \
|
||||||
|
-e 's/In general there are two types of tracks: data and audio./\ <BR>In general there are two types of tracks: data and audio./' \
|
||||||
|
-e 's/While audio tracks just contain a given/\ <BR>While audio tracks just contain a given/' \
|
||||||
|
-e 's/<b>Recordable CD Media:<\/b>/\ <BR><b>Recordable CD Media:<\/b>/' \
|
||||||
|
-e 's/<b>Overwriteable DVD Media:<\/b>/\ <BR><b>Overwriteable DVD Media:<\/b>/' \
|
||||||
|
-e 's/<b>Sequentially Recordable DVD Media:<\/b>/\ <BR><b>Sequentially Recordable DVD Media:<\/b>/' \
|
||||||
|
-e 's/<b>Drive preparation and addressing:<\/b>/\ <BR><b>Drive preparation and addressing:<\/b>/' \
|
||||||
|
-e 's/If you only got one CD capable drive/\ <BR>If you only got one CD capable drive/' \
|
||||||
|
-e 's/^Alphabetical list of options/\ <BR>Alphabetical list of options/' \
|
||||||
|
-e 's/and for all others\.<\/td><\/table>/and for all others.<\/td><\/table> <BR><HR><FONT SIZE=-1><CENTER>(HTML generated from '"$manpage"'.1 on '"$(date)"' by '$(basename "$0")' )<\/CENTER><\/FONT>/' \
|
||||||
|
-e 's/See section EXAMPLES/See section <A HREF="#EXAMPLES">EXAMPLES<\/A>/' \
|
||||||
|
<"$2" >"$htmlpage"
|
||||||
|
|
||||||
|
set +x
|
||||||
|
|
||||||
|
chmod u+rw,go+r,go-w "$htmlpage"
|
||||||
|
echo "Emerged file:"
|
||||||
|
ls -l "$htmlpage"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
export BROWSER='cp "%s" '"$raw_html"
|
||||||
|
man -H "$manpage"
|
||||||
|
"$0" -work_as_filter "$raw_html"
|
||||||
|
rm "$raw_html"
|
||||||
|
rm "$man_dir"/man1
|
||||||
|
|
||||||
|
fi
|
BIN
cdrskin/doener_150x200_tr.gif
Normal file
BIN
cdrskin/doener_150x200_tr.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
cdrskin/doener_150x200_tr_octx.gif
Normal file
BIN
cdrskin/doener_150x200_tr_octx.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@ -1,60 +1,66 @@
|
|||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
cdrskin Wiki - plain text copy
|
cdrskin Wiki - plain text copy
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
[[Image(source:/libburn/trunk/cdrskin/doener_150x200_tr.gif)]] [http://en.wikipedia.org/wiki/D%C3%B6ner_kebab Doener]
|
||||||
|
|
||||||
cdrskin is the cdrecord compatibility middleware of libburn.
|
'''cdrskin is the cdrecord compatibility middleware of libburn.'''
|
||||||
|
|
||||||
Its paragon, cdrecord, is a powerful GPL'ed burn program included in Joerg
|
Its paragon, cdrecord, is a powerful GPL'ed burn program included in Joerg
|
||||||
Schilling's cdrtools. cdrskin strives to be a second source for the services
|
Schilling's cdrtools. cdrskin strives to be a second source for the services
|
||||||
traditionally provided by cdrecord. Currently it does CD-R and CD-RW.
|
traditionally provided by cdrecord. Currently it does CD-R and CD-RW this way.
|
||||||
Its future ability to burn DVD media depends on the development of libburn.
|
Overwriteable media DVD-RAM, DVD+RW and DVD-RW are handled differently than
|
||||||
|
with cdrecord-ProDVD in order to offer TAO-like single track recording.
|
||||||
|
Sequential DVD-R[W] are handled like CD-R[W] with TAO and multi-session.
|
||||||
|
Additionally cdrskin offers cdrecord-ProDVD-like mode DAO with DVD-R[W].
|
||||||
|
|
||||||
cdrskin does not contain any bytes copied from cdrecord's sources.
|
cdrskin does not contain any bytes copied from cdrecord's sources.
|
||||||
Many bytes have been copied from the message output of cdrecord
|
Many bytes have been copied from the message output of cdrecord
|
||||||
runs, though. The most comprehensive technical overview of cdrskin
|
runs, though. The most comprehensive technical overview of cdrskin
|
||||||
can be found in cdrskin/README .
|
can be found in [http://libburnia.pykix.org/browser/libburn/trunk/cdrskin/README?format=txt cdrskin/README].
|
||||||
|
|
||||||
|
About libburn API for burning CD: http://libburnia-api.pykix.org
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
cdrskin with CD media fails to match its paragon cdrecord on three major
|
Appending sessions to an unclosed CD or DVD-R[W] is restricted to write mode
|
||||||
fields: convenient TAO burn mode, multi session, audio features.
|
TAO. (Users who have a burner which succeeds with a follow-up session on CD via
|
||||||
|
cdrecord -sao : please contact us.)
|
||||||
|
|
||||||
Audio features are the only topic where cdrskin did not yet exploit current
|
For DVD types other than DVD-RAM, DVD+RW, DVD-RW, DVD-R and for appending
|
||||||
libburn to the maximum. This is due to my own lack of audiophile motivation
|
sessions to ISO filesystems on DVD other than DVD-RW, DVD-R see the advise
|
||||||
and due to the lack of sincere users who provide me with cdrecord use cases,
|
to use dvd+rw-tools at the end of this text.
|
||||||
help me to explore the original cdrecord behavior and serve as dedicated
|
|
||||||
testers for eventual newly introduced cdrskin audio features.
|
|
||||||
|
|
||||||
cdrskin does not provide DVD burning yet. See advise to use dvd+rw-tools
|
|
||||||
at the end of this text.
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
About the command line options of cdrskin:
|
About the command line options of cdrskin:
|
||||||
|
|
||||||
|
They are described in detail in [http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html#OPTIONS section OPTIONS] of
|
||||||
|
[http://scdbackup.sourceforge.net/man_1_cdrskin_devel.html man cdrskin]
|
||||||
|
|
||||||
There are two families of options: cdrecord-compatible ones and options
|
There are two families of options: cdrecord-compatible ones and options
|
||||||
which are specific to cdrskin. The latter are mostly used to configure
|
which are specific to cdrskin. The latter are mostly used to configure
|
||||||
cdrskin for its task to emulate cdrecord. There are some, nevertheless,
|
cdrskin for its task to emulate cdrecord. There are some, nevertheless,
|
||||||
which provide rather exotic unique features of cdrskin.
|
which provide rather exotic unique features of cdrskin.
|
||||||
|
|
||||||
The cdrecord-compatible options are listed in the output of
|
The cdrecord-compatible options are listed in the output of
|
||||||
|
{{{
|
||||||
cdrskin -help
|
cdrskin -help
|
||||||
|
}}}
|
||||||
|
where the option "help" has *one* dash. Online: [http://scdbackup.sourceforge.net/cdrskin_help_devel cdrskin -help]
|
||||||
|
|
||||||
where the option "help" has *one* dash.
|
|
||||||
For these options you may expect program behavior that is roughly the
|
For these options you may expect program behavior that is roughly the
|
||||||
same as described in original man 1 cdrecord .
|
same as described in original man cdrecord .
|
||||||
|
|
||||||
Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
|
Online: http://cdrecord.berlios.de/old/private/man/cdrecord-2.0.html
|
||||||
|
|
||||||
The cdrskin-specific options are listed by
|
The cdrskin-specific options are listed by
|
||||||
|
{{{
|
||||||
cdrskin --help
|
cdrskin --help
|
||||||
|
}}}
|
||||||
|
where the option "help" has *two* dashes. Online: [http://scdbackup.sourceforge.net/cdrskin__help_devel cdrskin --help]
|
||||||
|
|
||||||
where the option "help" has *two* dashes.
|
Some are very experimental and should only be
|
||||||
|
used in coordination with the libburnia developer team.
|
||||||
Those have no man page yet. Some are very experimental and should only be
|
|
||||||
used in coordination with the libburn developer team.
|
|
||||||
Some are of general user interest, though:
|
Some are of general user interest, though:
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
@ -62,11 +68,10 @@ Some are of general user interest, though:
|
|||||||
--devices allows the sysadmin to scan the system for possible drives
|
--devices allows the sysadmin to scan the system for possible drives
|
||||||
and displays their detected properties.
|
and displays their detected properties.
|
||||||
The drives are listed one per line, with fields:
|
The drives are listed one per line, with fields:
|
||||||
|
libburn-drive-number, sysadmin-device-file, permissions, vendor, type
|
||||||
libburn-drive-number sysadmin-device-file permissions : vendor type
|
{{{
|
||||||
|
|
||||||
0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
0 dev='/dev/sg0' rwrw-- : 'HL-DT-ST' 'DVDRAM GSA-4082B'
|
||||||
|
}}}
|
||||||
This feature is valuable since cdrskin -scanbus will not give you
|
This feature is valuable since cdrskin -scanbus will not give you
|
||||||
the device file name and its current permissions.
|
the device file name and its current permissions.
|
||||||
cdrskin will accept of course the proposed dev= option as address
|
cdrskin will accept of course the proposed dev= option as address
|
||||||
@ -80,46 +85,45 @@ has to offer both, r- and w-permission.
|
|||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
eject_device=<path> is needed to work around yet broken tray ejection of
|
The DVD capabilities of cdrskin differ from those of cdrecord-ProDVD. cdrskin
|
||||||
drives. cdrskin makes a bold shell call to program "eject" and regrettably
|
lacks of support for DVD+R and for dual layer DVD media. On the other hand it
|
||||||
this program does not like our addresses for SCSI devices.
|
offers TAO-like multi-session with DVD-R[W] and TAO-like single session with
|
||||||
/dev/hdX work fine and /dev/sg0 is quite safely guess-translated to
|
overwriteable DVD media. It also offers DAO on DVD-R[W] which is probably the
|
||||||
/dev/sr0 . /dev/sg1 et.al. need the user's help. <path> must work with eject.
|
same as the traditional cdrecord-ProDVD write mode.
|
||||||
|
|
||||||
|
Non-cdrecord blank mode blank=format_overwrite brings a DVD-RW
|
||||||
|
disc from its initial profile "Sequential Recording" into profile state
|
||||||
|
"Restricted Overwrite".
|
||||||
|
{{{
|
||||||
|
cdrskin dev=/dev/sr0 -v blank=format_overwrite
|
||||||
|
}}}
|
||||||
|
|
||||||
|
DVD-RAM, DVD+RW and overwriteable DVD-RW appear to cdrskin as blank media
|
||||||
|
which are capable of taking only a single track. This track may be positioned
|
||||||
|
on a 32KiB aligned address, though.
|
||||||
|
{{{
|
||||||
|
cdrskin ... write_start_address=2412m ...
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Non-cdrecord blank mode blank=deformat_sequential brings an overwriteable
|
||||||
|
DVD-RW back into state "Sequential Recording" with the capability of doing
|
||||||
|
multi-session, if the drive is capable of "Incremental Streaming"
|
||||||
|
(MMC feature 21h).
|
||||||
|
|
||||||
|
Used sequential DVD-RW media may be blanked by blank=fast or blank=all which
|
||||||
|
normally both do full blanking.
|
||||||
|
|
||||||
|
blank=deformat_sequential does minimal blanking of DVD-RW which usually yields
|
||||||
|
media incapable of "Incremental Streaming".
|
||||||
|
|
||||||
|
Option --prodvd_cli_compatible activates blank=fast and blank=all for
|
||||||
|
overwriteable DVD-RW which normally ignore those two options. It also makes
|
||||||
|
option -multi tolerable with media and write modes which are not suitable for
|
||||||
|
multi-session. (The default behavior of cdrskin deems me to be preferrable.)
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
dev_translation=<sep><from><sep><to> is needed to foist cdrskin to frontend
|
fifo_start_at=<num> is a throughput enhancer for unsteady data streams
|
||||||
programs of cdrecord which do *not* ask cdrecord -scanbus but which make
|
|
||||||
own assumptions and guesses about cdrecord's device addresses.
|
|
||||||
cdrskin project - at least for now - refuses to try to provide a similar
|
|
||||||
guesswork but uses own cdrecord style addresses which have a mere
|
|
||||||
semi-automatic text mapping to real libburn addresses. See cdrskin/README,
|
|
||||||
"Pseudo-SCSI Adresses".
|
|
||||||
|
|
||||||
If you need to foist cdrskin under a frontend then you may be lucky and
|
|
||||||
both ideas of an address coincide. Especially if the frontend has the
|
|
||||||
decency to ask its "cdrecord" via option -scanbus for a list of drives.
|
|
||||||
If not, look into the error protocol of the frontend, look at the output
|
|
||||||
of a run of cdrskin --devices and give cdrskin the necessary hint.
|
|
||||||
|
|
||||||
If your frontend insists in using "0,0,0" and --devices reported
|
|
||||||
dev='/dev/sg0' resp. cdrskin -scanbus reported "1,0,0" then this
|
|
||||||
would be the appropriate translation
|
|
||||||
|
|
||||||
dev_translation=+0,0,0+/dev/sg0
|
|
||||||
|
|
||||||
The "+" character is a separator to be choosen by you.
|
|
||||||
Currently i am not aware of the need to choose any other than "+"
|
|
||||||
unless you get playful with custom translations like
|
|
||||||
|
|
||||||
dev_translation=-"cd+dvd"-1,0,0
|
|
||||||
|
|
||||||
See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html
|
|
||||||
for an illustrated example with K3b 0.10 .
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
--fifo_start_empty is a throughput enhancer for unsteady data streams
|
|
||||||
like they are produced by a compressing archiver program when piping to
|
like they are produced by a compressing archiver program when piping to
|
||||||
CD on-the-fly. It makes better use of the general property of a FIFO
|
CD on-the-fly. It makes better use of the general property of a FIFO
|
||||||
buffer to transport surplus bandwidth into the future. Yep. A time machine.
|
buffer to transport surplus bandwidth into the future. Yep. A time machine.
|
||||||
@ -143,10 +147,16 @@ underruns, of course.
|
|||||||
With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this
|
With a very fat fs=# buffer (128 MB for 12x CD is not unrealistic) this
|
||||||
can cause a big delay until burning finally starts and takes its due time.
|
can cause a big delay until burning finally starts and takes its due time.
|
||||||
|
|
||||||
--fifo_start_empty makes cdrskin start burning without waiting for the
|
fifo_start_at=<num> makes cdrskin start burning after the given number of bytes
|
||||||
FIFO to be full resp. the data stream to end. It can make use of the
|
is read rather than waiting for the FIFO to be completely full resp. the data
|
||||||
seconds spend with drive preparation and lead-in, it risks a few drive
|
stream to end. It risks a few drive buffer underruns at the beginning of burn
|
||||||
buffer underruns at the beginning of burn - but modern drives stand this.
|
- but modern drives stand this.
|
||||||
|
|
||||||
|
Usage examples:
|
||||||
|
{{{
|
||||||
|
cdrskin ... fs=128m fifo_start_at=20m ...
|
||||||
|
cdrskin ... fifo_start_at=0 ...
|
||||||
|
}}}
|
||||||
|
|
||||||
Note: no FIFO can give you better average throughput than the average
|
Note: no FIFO can give you better average throughput than the average
|
||||||
throughput of the data source and the throughput of the burner.
|
throughput of the data source and the throughput of the burner.
|
||||||
@ -162,26 +172,47 @@ default settings of cdrskin. Possible locations for such settings:
|
|||||||
|
|
||||||
/etc/opt/cdrskin/rc
|
/etc/opt/cdrskin/rc
|
||||||
|
|
||||||
|
/etc/cdrskin/cdrskin.conf
|
||||||
|
|
||||||
$HOME/.cdrskinrc
|
$HOME/.cdrskinrc
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
tao_to_sao_tsize=<num> allows the - actually unsupported - cdrecord option
|
dev_translation=<sep><from><sep><to> may be needed to foist cdrskin to
|
||||||
-tao and defines a default track size to be used if - as custom with -tao -
|
frontend programs of cdrecord which do *not* ask cdrecord -scanbus but
|
||||||
no option tsize=# is given.
|
which make own assumptions and guesses about cdrecord's device addresses.
|
||||||
|
|
||||||
As in general with cdrskin tsize=# the data source does not have to provide
|
Normally, cdrskin understands all addresses which are suitable for cdrecord
|
||||||
the full annouced amount of data. Missing data will be padded up by 0-bytes.
|
under Linux. See cdrskin/README, "Pseudo-SCSI Adresses".
|
||||||
Surplus data is supposed to cause an error, though. The burn will then
|
This option is mainly for (yet unknown) exotic configurations or very
|
||||||
be a failure in any way.
|
stubborn frontend programs.
|
||||||
|
|
||||||
|
If a frontend refuses to work with cdrskin, look into the error protocol
|
||||||
|
of that frontend, look at the output of a run of cdrskin --devices and give
|
||||||
|
cdrskin the necessary hint.
|
||||||
|
Example: Your frontend insists in using "0,0,0" and --devices reported
|
||||||
|
dev='/dev/hdc' resp. cdrskin dev=ATA -scanbus reported "1,0,0" then this
|
||||||
|
would be the appropriate translation:
|
||||||
|
{{{
|
||||||
|
dev_translation=+0,0,0+/dev/hdc
|
||||||
|
}}}
|
||||||
|
The "+" character is a separator to be choosen by you.
|
||||||
|
Currently i am not aware of the need to choose any other than "+"
|
||||||
|
unless you get playful with custom translations like
|
||||||
|
{{{
|
||||||
|
dev_translation=-"cd+dvd"-1,0,0
|
||||||
|
}}}
|
||||||
|
See http://scdbackup.sourceforge.net/k3b_on_cdrskin.html
|
||||||
|
for an illustrated example with K3b 0.10 .
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
DVD advise:
|
DVD advise:
|
||||||
|
|
||||||
For burning of DVD media the cdrskin project currently advises to use
|
For burning of DVD media other than DVD-RAM, DVD+RW, DVD-RW, DVD-R, the cdrskin
|
||||||
Andy Polyakov's dvd+rw-tools which despite their historic name burn
|
project currently advises to use Andy Polyakov's dvd+rw-tools which despite
|
||||||
for me on above burner: DVD+RW, DVD+R, DVD-RW, DVD-R .
|
their historic name burn for me on above burner: DVD-RAM, DVD+RW, DVD+R,
|
||||||
|
DVD-RW, DVD-R and are also capable of dual layer and even BD discs.
|
||||||
|
|
||||||
http://fy.chalmers.se/~appro/linux/DVD+RW/tools
|
http://fy.chalmers.se/~appro/linux/DVD+RW/tools
|
||||||
|
|
||||||
@ -189,24 +220,18 @@ They are not compatible or related to cdrecord resp. cdrecord-ProDVD
|
|||||||
(now obsoleted by original source cdrtools cdrecord with identical
|
(now obsoleted by original source cdrtools cdrecord with identical
|
||||||
capabilities besides the license key).
|
capabilities besides the license key).
|
||||||
|
|
||||||
If there is sincere and well motivated interest, the cdrskin project could try
|
A special feature of dvd+rw-tools is growing of ISO-9660 filesystems on
|
||||||
to employ growisofs as DVD burning engine. The cdrskin project would prefer to
|
overwriteable media. This is not the same as multi-session writing of cdrskin
|
||||||
wait for DVD support being included in libburn, though.
|
with CD media, but retrieves additional information from the existing ISO
|
||||||
A very limited and specialized cdrecord-compatibility wrapper for growisofs
|
image and finally manipulates the start sectors of this existing image.
|
||||||
serves in my project scdbackup. It is not overly hard to make one that serves
|
So for growable ISO filesystems on DVD-RAM or DVD+RW growisofs is the only
|
||||||
some very few fixed use cases.
|
choice, currently.
|
||||||
|
|
||||||
To my knowledge, Linux kernels 2.6 do write to DVD+RW via block devices as
|
|
||||||
they would write to a traditional tape device. Try old tape archiver
|
|
||||||
commands with addresses like /dev/sr0 or /dev/hdc rather than /dev/mt0 .
|
|
||||||
I have heard rumors that DVD-RW in mode "restricted overwrite" would be
|
|
||||||
block device ready, too. My burner is not a real friend of DVD-RW and
|
|
||||||
in an experiment the burn worked fine - but the result was not identical
|
|
||||||
to the stream sent to the device. I had similar failure with DVD-RAM, too.
|
|
||||||
|
|
||||||
Beware of the impact of a slow block device on overall system i/o buffering.
|
cdrskin can offer DVD multi-session only with sequential DVD-R[W]. Associated
|
||||||
It is wise to curb its input to a speed which it is able to deliver to media.
|
options blank=, -multi, -msinfo and -toc are available in this case. Thus
|
||||||
Else your i/o dedicated RAM might buffer a big amount of stream data.
|
sequential DVD-RW behave much like large CD-RW with possibly more than 99
|
||||||
|
tracks.
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
AC_INIT([libburn], [0.2.2], [http://libburn.pykix.org])
|
AC_INIT([libburn], [0.3.2], [http://libburnia.pykix.org])
|
||||||
AC_PREREQ([2.50])
|
AC_PREREQ([2.50])
|
||||||
dnl AC_CONFIG_HEADER([config.h])
|
dnl AC_CONFIG_HEADER([config.h])
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ dnl
|
|||||||
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
|
dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match
|
||||||
dnl
|
dnl
|
||||||
BURN_MAJOR_VERSION=0
|
BURN_MAJOR_VERSION=0
|
||||||
BURN_MINOR_VERSION=2
|
BURN_MINOR_VERSION=3
|
||||||
BURN_MICRO_VERSION=2
|
BURN_MICRO_VERSION=2
|
||||||
BURN_INTERFACE_AGE=0
|
BURN_INTERFACE_AGE=0
|
||||||
BURN_BINARY_AGE=0
|
BURN_BINARY_AGE=0
|
||||||
@ -57,6 +57,7 @@ AC_C_BIGENDIAN
|
|||||||
dnl Large file support
|
dnl Large file support
|
||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
AC_FUNC_FSEEKO
|
AC_FUNC_FSEEKO
|
||||||
|
AC_CHECK_FUNC([fseeko])
|
||||||
if test ! $ac_cv_func_fseeko; then
|
if test ! $ac_cv_func_fseeko; then
|
||||||
AC_ERROR([Libburn requires largefile support.])
|
AC_ERROR([Libburn requires largefile support.])
|
||||||
fi
|
fi
|
||||||
@ -73,6 +74,8 @@ THREAD_LIBS=-lpthread
|
|||||||
AC_SUBST(THREAD_LIBS)
|
AC_SUBST(THREAD_LIBS)
|
||||||
|
|
||||||
TARGET_SHIZZLE
|
TARGET_SHIZZLE
|
||||||
|
AC_SUBST(ARCH)
|
||||||
|
AC_SUBST(LIBBURN_ARCH_LIBS)
|
||||||
|
|
||||||
dnl Add compiler-specific flags
|
dnl Add compiler-specific flags
|
||||||
|
|
||||||
@ -98,6 +101,5 @@ AC_CONFIG_FILES([
|
|||||||
doc/doxygen.conf
|
doc/doxygen.conf
|
||||||
version.h
|
version.h
|
||||||
libburn-1.pc
|
libburn-1.pc
|
||||||
libisofs-1.pc
|
|
||||||
])
|
])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
94
doc/comments
94
doc/comments
@ -1,36 +1,41 @@
|
|||||||
/**
|
/**
|
||||||
@author Mario Danic, Thomas Schmitt
|
@author Mario Danic, Thomas Schmitt
|
||||||
|
|
||||||
@mainpage Libburn Documentation Index
|
@mainpage Libburnia Documentation Index
|
||||||
|
|
||||||
@section intro Introduction
|
@section intro Introduction
|
||||||
|
|
||||||
Libburn is an open-source library for reading, mastering and writing
|
Libburnia is an open-source project for reading, mastering and writing
|
||||||
optical discs. For now this means only CD-R and CD-RW.
|
optical discs. For now this means CD-R, CD-RW, DVD-RAM, DVD+RW, DVD-RW, DVD-R.
|
||||||
|
|
||||||
|
Not supported yet are DVD+R, any dual layer media, HD-DVD, BD (blue ray).
|
||||||
|
|
||||||
The project comprises of several more or less interdependent parts which
|
The project comprises of several more or less interdependent parts which
|
||||||
together strive to be a usable foundation for application development.
|
together strive to be a usable foundation for application development.
|
||||||
These are libraries, language bindings, and middleware binaries which emulate
|
These are libraries, language bindings, and middleware binaries which emulate
|
||||||
classical (and valuable) Linux tools.
|
classical (and valuable) Linux tools.
|
||||||
|
|
||||||
Our scope is currently Linux 2.4 and 2.6 and we will have a hard time to widen
|
Our scope is currently Linux 2.4 and 2.6 only. For ports to other systems
|
||||||
this for now, because of our history. The project could need advise from or
|
we would need : login on a development machine resp. a live OS on CD or DVD,
|
||||||
membership of skilled kernel people and people who know how to talk CD/DVD
|
advise from a system person about the equivalent of Linux sg or FreeBSD CAM,
|
||||||
drives into doing things.
|
volunteers for testing of realistic use cases.
|
||||||
|
|
||||||
We do have a workable code base for burning data CDs, though. The burn API is
|
We have a workable code base for burning data and audio CDs and many DVD types.
|
||||||
quite comprehensively documented and can be used to build a presentable
|
The burn API is quite comprehensively documented and can be used to build a
|
||||||
application.
|
presentable application.
|
||||||
We do have a functional binary which emulates parts of cdrecord in order to
|
We have a functional binary which emulates the core use cases of cdrecord in
|
||||||
prove that usability, and in order to allow you to explore libburn's scope
|
order to prove that usability, and in order to allow you to explore libburn's
|
||||||
by help of existing cdrecord frontends.
|
scope by help of existing cdrecord frontends.
|
||||||
|
|
||||||
@subsection components The project components (list subject to growth, hopefully):
|
@subsection components The project components (list subject to growth, hopefully):
|
||||||
|
|
||||||
- libburn is the library by which preformatted data get onto optical media.
|
- 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
|
It uses either /dev/sgN (e.g. on kernel 2.4 with ide-scsi) or
|
||||||
/dev/hdX (e.g. on kernel 2.6).
|
/dev/hdX (e.g. on kernel 2.6).
|
||||||
libburn is the foundation of our cdrecord emulation.
|
libburn is the foundation of our cdrecord emulation. Its code is
|
||||||
|
independent of cdrecord. Its DVD capabilities are learned from
|
||||||
|
studying the code of dvd+rw-tools and MMC-5 specs. No code but only
|
||||||
|
the pure SCSI knowledge has been taken from dvd+rw-tools, though.
|
||||||
|
|
||||||
- libisofs is the library to pack up hard disk files and directories into a
|
- 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.
|
ISO 9660 disk image. This may then be brought to CD via libburn.
|
||||||
@ -39,15 +44,18 @@ by help of existing cdrecord frontends.
|
|||||||
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
|
- cdrskin is a limited cdrecord compatibility wrapper for libburn.
|
||||||
cdrecord is a powerful GPL'ed burn program included in Joerg
|
cdrecord is a powerful GPL'ed burn program included in Joerg
|
||||||
Schilling's cdrtools. cdrskin strives to be a second source for
|
Schilling's cdrtools. cdrskin strives to be a second source for
|
||||||
the services traditionally provided by cdrecord.
|
the services traditionally provided by cdrecord. Additionally it
|
||||||
|
provides libburn's DVD capabilities, where only -sao is compatible
|
||||||
|
with cdrecord.
|
||||||
cdrskin does not contain any bytes copied from cdrecord's sources.
|
cdrskin does not contain any bytes copied from cdrecord's sources.
|
||||||
Many bytes have been copied from the message output of cdrecord
|
Many bytes have been copied from the message output of cdrecord
|
||||||
runs, though.
|
runs, though.
|
||||||
See cdrskin/README for more.
|
See cdrskin/README for more.
|
||||||
|
|
||||||
- "test" is a collection of application gestures and examples given by the
|
- "test" is a collection of application gestures and examples given by the
|
||||||
authors of the library features. The main API example of libburn
|
authors of the library features. The burn API example of libburn
|
||||||
is named test/libburner.c .
|
is named test/libburner.c . The API for media information inquiry is
|
||||||
|
demonstrated in test/telltoc.c .
|
||||||
Explore these examples if you look for inspiration.
|
Explore these examples if you look for inspiration.
|
||||||
|
|
||||||
We plan to be a responsive upstream. Bear with us.
|
We plan to be a responsive upstream. Bear with us.
|
||||||
@ -80,37 +88,55 @@ languages and development tools.
|
|||||||
|
|
||||||
libburner is a minimal demo application for the library libburn
|
libburner is a minimal demo application for the library libburn
|
||||||
(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
|
(see: libburn/libburn.h) as provided on http://libburn.pykix.org .
|
||||||
It can list the available devices, can blank a CD-RW and
|
It can list the available devices, can blank a CD-RW or DVD-RW and
|
||||||
can burn to CD-R or CD-RW.
|
can burn to CD-R, CD-RW, DVD-RAM, DVD+RW, DVD-RW, DVD-R.
|
||||||
|
|
||||||
It's main purpose, nevertheless, is to show you how to use libburn and also
|
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
|
to serve the libburnia team as reference application. libburner does indeed
|
||||||
define the standard way how above three gestures can be implemented and
|
define the standard way how above three gestures can be implemented and
|
||||||
stay upward compatible for a good while.
|
stay upward compatible for a good while.
|
||||||
|
|
||||||
@subsection libburner-help Libburner --help
|
@subsection libburner-help Libburner --help
|
||||||
<pre>
|
<pre>
|
||||||
Usage: test/libburner
|
Usage: test/libburner
|
||||||
[--drive <address>|<driveno>|"-"]
|
[--drive <address>|<driveno>|"-"] [--audio]
|
||||||
[--verbose <level>] [--blank_fast|--blank_full]
|
[--blank_fast|--blank_full|--format_overwrite]
|
||||||
[--burn_for_real|--try_to_simulate] [--stdin_size <bytes>]
|
[--try_to_simulate]
|
||||||
[<imagefile>|"-"]
|
[--multi] [<one or more imagefiles>|"-"]
|
||||||
Examples
|
Examples
|
||||||
A bus scan (needs rw-permissions to see a drive):
|
A bus scan (needs rw-permissions to see a drive):
|
||||||
test/libburner --drive -
|
test/libburner --drive -
|
||||||
Burn a file to drive chosen by number:
|
Burn a file to drive chosen by number, leave appendable:
|
||||||
test/libburner --drive 0 --burn_for_real my_image_file
|
test/libburner --drive 0 --multi my_image_file
|
||||||
Burn a file to drive chosen by persistent address:
|
Burn a file to drive chosen by persistent address, close:
|
||||||
test/libburner --drive /dev/hdc --burn_for_real my_image_file
|
test/libburner --drive /dev/hdc my_image_file
|
||||||
Blank a used CD-RW (is combinable with burning in one run):
|
Blank a used CD-RW (is combinable with burning in one run):
|
||||||
test/libburner --drive 0 --blank_fast
|
test/libburner --drive /dev/hdc --blank_fast
|
||||||
Burn a compressed afio archive on-the-fly, pad up to 700 MB:
|
Blank a used DVD-RW (is combinable with burning in one run):
|
||||||
|
test/libburner --drive /dev/hdc --blank_full
|
||||||
|
Format a DVD-RW to avoid need for blanking before re-use:
|
||||||
|
test/libburner --drive /dev/hdc --format_overwrite
|
||||||
|
Burn two audio tracks (to CD only):
|
||||||
|
lame --decode -t /path/to/track1.mp3 track1.cd
|
||||||
|
test/dewav /path/to/track2.wav -o track2.cd
|
||||||
|
test/libburner --drive /dev/hdc --audio track1.cd track2.cd
|
||||||
|
Burn a compressed afio archive on-the-fly:
|
||||||
( cd my_directory ; find . -print | afio -oZ - ) | \
|
( cd my_directory ; find . -print | afio -oZ - ) | \
|
||||||
test/libburner --drive /dev/hdc --burn_for_real --stdin_size 734003200 -
|
test/libburner --drive /dev/hdc -
|
||||||
To be read from *not mounted* CD via:
|
To be read from *not mounted* media via: afio -tvZ /dev/hdc
|
||||||
afio -tvZ /dev/hdc
|
|
||||||
Program tar would need a clean EOF which our padded CD cannot deliver.
|
|
||||||
</pre>
|
</pre>
|
||||||
|
libburner has two companions, telltoc and dewav, which help to perform some
|
||||||
|
peripheral tasks of burning.
|
||||||
|
|
||||||
|
telltoc prints a table of content (sessions, tracks and leadouts), it tells
|
||||||
|
about type and state of media, and also is able to provide the necessary
|
||||||
|
multi-session information for program mkisofs option -C. Especially helpful
|
||||||
|
are its predictions with "Write multi" and "Write modes" where availability
|
||||||
|
of "TAO" indicates that tracks of unpredicted length can be written.
|
||||||
|
See: test/telltoc --help.
|
||||||
|
|
||||||
|
dewav extracts raw byte-swapped audio data from files of format .wav (MS WAVE)
|
||||||
|
or .au (SUN Audio). See example in libburner --help.
|
||||||
|
|
||||||
@subsection libburner-source Sourceode of libburner
|
@subsection libburner-source Sourceode of libburner
|
||||||
|
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
/**
|
|
||||||
@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
|
|
||||||
*/
|
|
966
doc/cookbook.txt
Normal file
966
doc/cookbook.txt
Normal file
@ -0,0 +1,966 @@
|
|||||||
|
libburnia.pykix.org Optical Media Rotisserie Recipes as of February 2007
|
||||||
|
|
||||||
|
Content:
|
||||||
|
- TAO Multi-Session CD Cookbook (CD-R, CD-RW)
|
||||||
|
- SAO CD Cookbook (CD-R, CD-RW, pure audio or pure data only)
|
||||||
|
- Overwriteable DVD Cookbook (DVD-RAM, DVD+RW, DVD-RW)
|
||||||
|
- Sequential DVD-R[W] Cookbook
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
TAO Multi-Session CD Cookbook
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Guided by reading mmc-r10a.pdf , O.8 "Write a Track"
|
||||||
|
from http://www.t10.org/ftp/t10/drafts/mmc/
|
||||||
|
backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/
|
||||||
|
by reading spc3r23.pdf from http://www.t10.org/ftp/t10/drafts/spc3/
|
||||||
|
by reading libburn/* from http://icculus.org/burn
|
||||||
|
and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S
|
||||||
|
which used in part code from http://icculus.org/burn.
|
||||||
|
|
||||||
|
For libburnia.pykix.org by Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Media type can be recognized by Current Profile from 46h GET CONFIGURATION.
|
||||||
|
(mmc5r03c.pdf 6.6.2.1)
|
||||||
|
|
||||||
|
CD-R 0009h
|
||||||
|
CD-RW 000ah
|
||||||
|
|
||||||
|
The following topics are covered in this text:
|
||||||
|
- About blank, appendable and finalized CD media
|
||||||
|
- Writing a session to CD in TAO mode
|
||||||
|
- Obtaining CD multi-session info for extending ISO-9660 filesystems
|
||||||
|
- Obtaining a Table Of Content from CD
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
About blank, appendable and finalized CD media :
|
||||||
|
|
||||||
|
CD media have to be blank or appendable in order to be writeable in TAO mode.
|
||||||
|
The according status may be inquired by 51h READ DISC INFORMATION requesting
|
||||||
|
Data Type 000b Standard Disc Information, where reply value Disc Status
|
||||||
|
indicates:
|
||||||
|
00b blank
|
||||||
|
01b appendable
|
||||||
|
10b finalized
|
||||||
|
11b others (unsuitable for this recipe)
|
||||||
|
(mmc5r03c.pdf 6.22.3.1.4)
|
||||||
|
|
||||||
|
CD-RW which are finalized or appendable may be blanked by command A1h BLANK
|
||||||
|
with blanking types 000b "Blank the disc" or 001b "Minimally blank the disc".
|
||||||
|
The Start Address/Track Number will be ignored so it may well be 0.
|
||||||
|
|
||||||
|
Because the operation is long running it is advised to set the Immed bit and to
|
||||||
|
watch the progress by commands 00h TEST UNIT READY and 03h REQUEST SENSE
|
||||||
|
with DESC bit set to 0 for fixed format reply.
|
||||||
|
It is done when 00h succeeds and 03h reports 0 in PROGRESS INDICATION
|
||||||
|
(byte 1+2 in Table 22 = byte 16+17 SENSE KEY SPECIFIC in table 26).
|
||||||
|
(mmc5r03c.pdf 6.2 BLANK)
|
||||||
|
(spc3r23.pdf 4.5.2.4.4 table 22, 4.5.3 table 26,
|
||||||
|
6.27 REQUEST SENSE, 6.33 TEST UNIT READY)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Writing a session to CD in TAO mode :
|
||||||
|
|
||||||
|
The writing method for blank or appendable media is the same. A new session
|
||||||
|
will get created automatically by the first track when it is written. If the
|
||||||
|
media is blank then the new session will be the first and only one in the
|
||||||
|
table of content. If the media is appendable then a new session will be
|
||||||
|
appended to the existing sessions. In any case the new track will be the
|
||||||
|
first one in the new session.
|
||||||
|
|
||||||
|
Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that
|
||||||
|
kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should
|
||||||
|
be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the
|
||||||
|
next lower possible value by the drive. So it is helpful to add a few
|
||||||
|
kbytes/sec just in case the drive has rounding problems.
|
||||||
|
(mmc5r03c.pdf 6.37)
|
||||||
|
|
||||||
|
Before writing can occur, a Write Parameters mode page 05h has to be composed
|
||||||
|
and transmitted via 55h MODE SELECT. Mode page 05h describes several burn
|
||||||
|
parameters:
|
||||||
|
BUFE Buffer Underrun protection 0=off, 1=on
|
||||||
|
Test Write -dummy mode for writing 0=off, 1=on
|
||||||
|
Write Type Packet/TAO/SAO/RAW 01h = TAO
|
||||||
|
Multi-session Wether to keep appendable 00b = finalize
|
||||||
|
11b = keep appendable
|
||||||
|
Track Mode Describes frame type 4 for data , 0 for audio
|
||||||
|
Data Block Type Layout of payload blocks 8 for 2048 byte data blocks
|
||||||
|
0 for 2352 byte audio blocks
|
||||||
|
Audio Pause Length 150 = 2 seconds
|
||||||
|
Any other parameters may be set to 0.
|
||||||
|
Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header
|
||||||
|
as of SPC-3 table 240. This 8-byte header may be filled with zeros.
|
||||||
|
(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode)
|
||||||
|
(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats)
|
||||||
|
|
||||||
|
|
||||||
|
Writing has to begin at the address returned by 52h READ TRACK INFORMATION
|
||||||
|
with Address/Number Type set to 01b and Logical Block Address/Track/Session
|
||||||
|
Number set to FFh. The Next Writeable Address as of table 500 is the number
|
||||||
|
to start writing with.
|
||||||
|
(mmc5r03c.pdf 6.27 )
|
||||||
|
|
||||||
|
Writing is performed by one or more 2Ah WRITE transactions with the Logical
|
||||||
|
Block Address counted up from the initial number in sync with the number of
|
||||||
|
blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be
|
||||||
|
added to the Logical Block Address for the next 2Ah WRITE. Only full blocks
|
||||||
|
can be written.
|
||||||
|
(mmc5r03c.pdf, 6.44)
|
||||||
|
When writing is done, it is mandatory to force the drive's buffer to media by
|
||||||
|
35h SYNCHRONIZE CACHE.
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
A track must at least contain 300 payload blocks: 4 seconds of audio or
|
||||||
|
600 KiB of data.
|
||||||
|
(mmc5r03c.pdf 6.3.3.1.2)
|
||||||
|
|
||||||
|
The track has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b.
|
||||||
|
Older MMC specifies a valid Logical Track Number FFh to depict the open track.
|
||||||
|
MMC-5 is quite silent about this. FFh works for my drives.
|
||||||
|
(mmc5r03c.pdf 6.3.3.1.2)
|
||||||
|
|
||||||
|
After that, a new track may be written beginning with sending the mode page 05h
|
||||||
|
again. It is not tested wether 05h can be omitted if Track Mode and Data Block
|
||||||
|
Type are the same as with the previous track.
|
||||||
|
The new track will be added to the session which was opened by the first track.
|
||||||
|
|
||||||
|
After the last track of a session, 5Bh CLOSE TRACK SESSION Close Function 010b
|
||||||
|
with Logical Track Number 0 closes the session. It depends on the Multi-Session
|
||||||
|
value in mode page 05h wether the disc is finalized or stays appendable.
|
||||||
|
(mmc5r03c.pdf 6.3.3.1.3)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Obtaining CD multi-session info for extending ISO-9660 filesystems :
|
||||||
|
|
||||||
|
Program mkisofs expects two numbers with its option -C which describe the
|
||||||
|
situation on an appendable CD which already contains a ISO-9660 filesystem
|
||||||
|
in the first track of the last session.
|
||||||
|
|
||||||
|
The first number is the Logical Block Address of that track containing the
|
||||||
|
existing ISO-9660 filesystem image. This number is needed for mkisofs option
|
||||||
|
-M to connect to the existing image. The new image will refer to files in
|
||||||
|
the previously existing image. mkisofs option -M needs read access to the
|
||||||
|
CD or a blockwise copy of it on hard disk.
|
||||||
|
The number is gained by 43h READ TOC/PMA/ATIP.
|
||||||
|
(mmc5r03c.pdf 6.26)
|
||||||
|
Untested is Format 0001b which in table 478 promises quick access via
|
||||||
|
Start Address Of First Track In Last Session.
|
||||||
|
(mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1)
|
||||||
|
libburn gets the number from its Table Of Content model which is obtained
|
||||||
|
by 43h READ TOC/PMA/ATIP, Format 0010b. See below.
|
||||||
|
|
||||||
|
The second number is an exact prediction of the Logical Block Address of the
|
||||||
|
new track which will contain the newly generated ISO-9660 image.
|
||||||
|
Even without mkisofs option -M this second number is still needed to make the
|
||||||
|
inner block address pointers of the image match the Logical Block Addresses
|
||||||
|
on CD. For that one may inquire 52h READ TRACK INFORMATION with
|
||||||
|
Address/Number Type set to 01b and Logical Block Address/Track/Session
|
||||||
|
Number set to FFh. The Next Writeable Address as of table 500 is the number
|
||||||
|
to use.
|
||||||
|
(mmc5r03c.pdf 6.27 )
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Obtaining a Table Of Content from CD :
|
||||||
|
|
||||||
|
The structure of a CD is comprised of sessions. Each session contains one or
|
||||||
|
more tracks and is followed by a lead-out. A track has an address and a length.
|
||||||
|
|
||||||
|
Table of content information is gained by 43h READ TOC/PMA/ATIP, Format 0010b.
|
||||||
|
(mmc5r03c.pdf 6.26.2.5 table 478)
|
||||||
|
|
||||||
|
The number of sessions is given by Last Complete Session Number.
|
||||||
|
The number of TOC Track descriptors is: (TOC Data Length - 2)/11 .
|
||||||
|
|
||||||
|
Each TOC Track Descriptor contains a Session Number.
|
||||||
|
|
||||||
|
If POINT is >= 1 and <= 99 (63h) then the descriptor is about the track of
|
||||||
|
which POINT tells the number.
|
||||||
|
The start address of this track can be read from PMIN, PSEC, PFRAME where
|
||||||
|
it is encoded in MSF format:
|
||||||
|
blocks = frames - 150, 75 frames = 1 sec , 60 sec = 1 min.
|
||||||
|
The length of the track is given by MIN,SEC,FRAME in the same format.
|
||||||
|
|
||||||
|
If POINT = A0h then the descriptor tells in PMIN the first track number of its
|
||||||
|
session.
|
||||||
|
POINT = A1h tells in PMIN the last track number of its session.
|
||||||
|
POINT = A2h describes in PMIN, PSEC, PFRAME the lead-out of a session, i.e the
|
||||||
|
first address after the session's end. (Next writeable address typically is
|
||||||
|
lead-out + 11400 after the first session, lead-out + 6900 after further
|
||||||
|
sessions.)
|
||||||
|
POINT = B0h tells in MIN,SEC,FRAME this next writeable address or FFh,FFh,FFh
|
||||||
|
for finalized disc.
|
||||||
|
(mmc5r03c.pdf 6.26.3.4 table 489, 4.2.3.7 Mode-1 Q, Mode-5 Q)
|
||||||
|
|
||||||
|
|
||||||
|
In libburn the address of the first track in the last session is obtained from
|
||||||
|
the last session's POINT = A0h and from the track descriptor with the POINT
|
||||||
|
value matching the PMIN value of the A0h descriptor.
|
||||||
|
Untested is wether POINT = B0h and 52h READ TRACK INFORMATION are always in
|
||||||
|
sync. libburn uses the info provided by 52h READ TRACK INFORMATION.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
SAO CD Cookbook
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Guided by reading libburn/* from http://icculus.org/burn
|
||||||
|
backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/
|
||||||
|
and by experiments with drives NEC ND-4570A, LG GSA-4082B, LITE-ON LTR48125S
|
||||||
|
which used in part code from http://icculus.org/burn.
|
||||||
|
|
||||||
|
For libburnia.pykix.org by Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Recognition of media type and state (blank, appendable, finalized) is as
|
||||||
|
described in the TAO Multi-Session CD Cookbook. See there.
|
||||||
|
|
||||||
|
The MMC specs do not give much hint about the combination of SAO and
|
||||||
|
multi-session. My drives refused not only on a few experiments which i did
|
||||||
|
in libburn but also failed with cdrecord -sao on an appendable CD.
|
||||||
|
So for now only blank CD seem to be suitable for SAO writing.
|
||||||
|
|
||||||
|
Different from TAO mode, the whole session layout is announced to the drive by
|
||||||
|
sending a Cue Sheet. This implies that the sizes of the tracks have to be known
|
||||||
|
in advance, which is a heavy drawback when dealing with track data sources like
|
||||||
|
stdin, named pipes or sockets.
|
||||||
|
Nevertheless, SAO seems to be best writing mode for audio purposes, as our
|
||||||
|
audio expert Lorenzo Taylor found out.
|
||||||
|
|
||||||
|
A SAO session in libburn may either consist entirely of audio tracks or
|
||||||
|
entirely of data tracks. For mixed sessions, only TAO is usable yet.
|
||||||
|
|
||||||
|
- Composing a SAO CD Cue Sheet (either audio or data, but not mixed)
|
||||||
|
- Writing the prepared SAO CD session
|
||||||
|
- What is known about mixed mode sessions
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Composing a Cue Sheet (either audio or data, but not mixed) :
|
||||||
|
|
||||||
|
The Cue Sheet will get submitted to the drive by 5Dh SEND CUE SHEET.
|
||||||
|
Each entry of the sheet is of 8 bytes size. Its fields are named
|
||||||
|
CTL|ADR, TNO, INDEX, DATA FORM, SCMS, MIN, SEC, FRAME .
|
||||||
|
(mmc5r03c.pdf 6.33)
|
||||||
|
|
||||||
|
CTL is 40h for data and 00h for audio.
|
||||||
|
(mmc5r03c.pdf 6.33.3.4)
|
||||||
|
ADR is always 01h.
|
||||||
|
TNO is the track number (1 to 99).
|
||||||
|
INDEX is a subaddress within tracks. This recipe uses only INDEX 01h within
|
||||||
|
tracks.
|
||||||
|
(mmc5r03c.pdf 4.2.3.5.2)
|
||||||
|
DATA FORM is 00h for audio payload , 10h for data. (01h for audio pause is not
|
||||||
|
used in libburn).
|
||||||
|
(mmc5r03c.pdf 6.33.3.11 CD-DA Data Form, 6.33.3.12 CD-ROM mode 1 Form)
|
||||||
|
SCMS is always 00h.
|
||||||
|
MIN, SEC, FRAME give the MSF address where the described data entity starts.
|
||||||
|
LBA = frames - 150, 75 frames = 1 sec , 60 sec = 1 min.
|
||||||
|
This address must increase from entry to entry (or at least stay equal).
|
||||||
|
|
||||||
|
|
||||||
|
The first entry describes the Lead-in. Its content is
|
||||||
|
(CTL|ADR ,00h,00h,01h,00h,00h,00h,00h)
|
||||||
|
With the CTL|ADR for the first track: 41h for data, 01h for audio.
|
||||||
|
|
||||||
|
The LBA for the first write is negative: -150. This corresponds to MSF address
|
||||||
|
00h:00h:00h. All addresses are to be given in MSF format.
|
||||||
|
The first information track on disc is preceded by a pause encoding of 2 sec:
|
||||||
|
(CTL|ADR,01h,00h, DATA FORM ,00h,00h,00h,00h)
|
||||||
|
with DATA FORM = 00h for audio and 10h for data. By those 2 seconds the MSF
|
||||||
|
address increases to 00h:02h:00h = LBA 0.
|
||||||
|
|
||||||
|
Each track is represented by an entry
|
||||||
|
(CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME)
|
||||||
|
TNO gives the track number. MIN, SEC, FRAME give the MSF address which becomes
|
||||||
|
the start address of the track. The MSF address is then increased by the size
|
||||||
|
of the track (to be used with next track or with lead-out).
|
||||||
|
|
||||||
|
A track must at least contain 300 payload blocks: 4 seconds of audio or
|
||||||
|
600 KiB of data.
|
||||||
|
(mmc5r03c.pdf 6.33.3.6)
|
||||||
|
|
||||||
|
At the end of the session there is a lead-out entry
|
||||||
|
(CTL|ADR,AAh,01h,01h,00h,MIN,SEC,FRAME)
|
||||||
|
marking the end of the last track. (With libburn CTL is as of the last track.)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Writing the prepared session :
|
||||||
|
|
||||||
|
Speed may be set by BBh SET CD SPEED parameter Drive Write Speed. Note that
|
||||||
|
kbytes/sec means 1000 bytes/sec and not 1024/sec. Rotational control should
|
||||||
|
be set to 00b. 1x CD speed is 176.4 kbytes/sec. Speed is usually set to the
|
||||||
|
next lower possible value by the drive. So it is helpful to add a few
|
||||||
|
kbytes/sec just in case the drive has rounding problems.
|
||||||
|
(mmc5r03c.pdf 6.37)
|
||||||
|
|
||||||
|
A Write Parameters mode page 05h has to be composed and transmitted via
|
||||||
|
55h MODE SELECT. This page describes the following parameters:
|
||||||
|
BUFE Buffer Underrun protection 0=off, 1=on
|
||||||
|
Test Write -dummy mode for writing 0=off, 1=on
|
||||||
|
Write Type Packet/TAO/SAO/RAW 02h = SAO
|
||||||
|
Multi-session Wether to keep appendable 00b = finalize
|
||||||
|
11b = keep appendable
|
||||||
|
Track Mode Describes frame type 0 (is ignored)
|
||||||
|
Data Block Type Layout of payload blocks 0 (is ignored)
|
||||||
|
Audio Pause Length 150 = 2 seconds (ignored ?)
|
||||||
|
Any other parameters may be set to 0.
|
||||||
|
Mode page data as of MMC-5 table 644 are preceded by a Mode Parameter Header
|
||||||
|
as of SPC-3 table 240. This 8-byte header may be filled with zeros.
|
||||||
|
(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode)
|
||||||
|
(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats)
|
||||||
|
|
||||||
|
The Cue Sheet is submitted to the drive by 5Dh SEND CUE SHEET. Cue Sheet Size
|
||||||
|
is 8 times the number of entries.
|
||||||
|
(mmc5r03c.pdf 6.33)
|
||||||
|
|
||||||
|
Writing is performed by multiple 2Ah WRITE transactions with the Logical
|
||||||
|
Block Address counted up from the initial number in sync with the number of
|
||||||
|
blocks written. I.e the Transfer Length of the previous 2Ah WRITE has to be
|
||||||
|
added to the Logical Block Address for the next 2Ah WRITE. Only full blocks
|
||||||
|
can be written.
|
||||||
|
(mmc5r03c.pdf, 6.44)
|
||||||
|
Writing begins at LBA -150 which is to be transmitted as 4-byte, Big-endian,
|
||||||
|
two's-complement. E.g: -150 = FFh FFh FFh 6Ah. This is the natural form found
|
||||||
|
with about any 32-bit processor, so only the endianness has to be taken into
|
||||||
|
respect when converting a 32-bit integer into a LBA for command 2Ah WRITE.
|
||||||
|
|
||||||
|
|
||||||
|
At first the mandatory pause preceding the first track has to be written as
|
||||||
|
150 blocks of the matching sector size: 2048 for data, 2352 for audio.
|
||||||
|
By this, the LBA increases from -150 to 0.
|
||||||
|
|
||||||
|
Next the tracks' payload is sent. For each track exactly the number of blocks
|
||||||
|
has to be transmitted as is announced in the Cue Sheet by the difference
|
||||||
|
of the track's own start address and the start address of the next entry in
|
||||||
|
the Cue Sheet. After each write the LBA for the next write has to be increased
|
||||||
|
by the number of blocks transmitted. Just like with TAO writing.
|
||||||
|
|
||||||
|
There is no separator between the tracks of a pure mode SAO session.
|
||||||
|
(If the session was mixed mode, there would be extended Pre-gaps and Post-gaps
|
||||||
|
between data mode tracks and audio mode tracks.)
|
||||||
|
(libburn sends its own buffer to the drive at the end of each track but does
|
||||||
|
not sync the drive's chache. It is unclear wether this separation of tracks
|
||||||
|
on the level of 2Ah WRITE is necessary with a pure mode session. It does not
|
||||||
|
harm in any case and would probably be unavoidable if audio and data tracks
|
||||||
|
were mixed.)
|
||||||
|
|
||||||
|
When writing of all tracks is done, it is mandatory to force the drive's buffer
|
||||||
|
to media by 35h SYNCHRONIZE CACHE.
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.)
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
What is known about mixed mode sessions :
|
||||||
|
|
||||||
|
For now, SAO sessions with a mix of data and audio are not supported in
|
||||||
|
libburn. Here are the reasons why.
|
||||||
|
|
||||||
|
Obviously the code of http://icculus.org/burn is incomplete in this aspect.
|
||||||
|
In mmc5r03c.pdf comparison of table 555 and 6.33.3.18 seems self-contradicting.
|
||||||
|
(The second Pre-gap in table 555 does not match any of the criteria of
|
||||||
|
6.33.3.18. Also, there is no Post-gap shown in table 555 although 6.33.3.19
|
||||||
|
would prescribe some.)
|
||||||
|
|
||||||
|
If a data track follows an audio track then the data track gets a preceding
|
||||||
|
extended Pre-gap:
|
||||||
|
(CTL|ADR, TNO ,01h,DATA FORM,00h, MIN , SEC , FRAME)
|
||||||
|
with TNO already the number of the data track. The MSF address is to be
|
||||||
|
increased by 3 seconds. The first second of the extended Pre-gap needs to be
|
||||||
|
written in the audio track's mode and the other 2 seconds are to be written
|
||||||
|
in the data track's mode.
|
||||||
|
(libburn compares DATA FORM rather than burn_track.mode . Wrong ?)
|
||||||
|
(libburn currently does only 2 seconds and the second part of Pre-gap. There is
|
||||||
|
an issue with burn_track.pregap1 about this. Seems libburn mistakes the pause
|
||||||
|
preceding track 1 for a part 2 of an extended Pre-gap.)
|
||||||
|
|
||||||
|
If a data track is followed by an audio track then it gets a Post-gap of at
|
||||||
|
least two seconds.
|
||||||
|
No example of Post-gap is given for Cue Sheet. Maybe it is to be added to the
|
||||||
|
track, or maybe it gets an own Cue Sheet entry ... who knows ?
|
||||||
|
(libburn contains write code for pregap1, pregap2 and postgap. But only
|
||||||
|
pregap2 ever gets activated. Once hackingly for the first 2 second pause, once
|
||||||
|
incompletely for a change of DATA FORM.)
|
||||||
|
|
||||||
|
Seems nobody ever tested this. Libburnia simply knows no use case where the
|
||||||
|
correctness of Pre-gap and Post-gap would become evident.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Overwriteable DVD Cookbook
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools ,
|
||||||
|
backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/
|
||||||
|
and by experiments with drives NEC ND-4570A and LG GSA-4082B.
|
||||||
|
|
||||||
|
For libburnia.pykix.org by Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Media type can be recognized by Current Profile from 46h GET CONFIGURATION.
|
||||||
|
(mmc5r03c.pdf 6.6.2.1)
|
||||||
|
|
||||||
|
DVD-RAM 0012h
|
||||||
|
DVD-RW Restricted Overwrite 0013h
|
||||||
|
DVD-RW Sequential Recording 0014h (i.e. unformatted)
|
||||||
|
DVD+RW 001Ah
|
||||||
|
|
||||||
|
A short compilation of the write model:
|
||||||
|
- Overwriting in general
|
||||||
|
|
||||||
|
The recipes described here are depending on formatting state:
|
||||||
|
- DVD-RAM, fully formatted DVD+RW or DVD-RW
|
||||||
|
- Unformatted DVD+RW
|
||||||
|
- Partly formatted DVD+RW
|
||||||
|
- Unformatted DVD-RW
|
||||||
|
- Partly formatted DVD-RW
|
||||||
|
- Intermediate state DVD-RW
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Overwriting in general :
|
||||||
|
|
||||||
|
Depending on media type, some kind of formatting has to have happened before
|
||||||
|
data can be written. Formatting may happen separately from writing or
|
||||||
|
simultaneously. See the particular recipes below.
|
||||||
|
|
||||||
|
No Write Parameters mode page 05h is to be sent. Speed can be influenced by
|
||||||
|
B6h SET STREAMING , speed capabilities can be inquired by ACh GET PERFORMANCE.
|
||||||
|
It is advised to set only speeds and sizes which are returned by ACh.
|
||||||
|
(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE)
|
||||||
|
Optimal performance is promised without any speed setting. But my experiments
|
||||||
|
showed that SET STREAMING values persist after media change.
|
||||||
|
|
||||||
|
In the formatted area of the media, coarse random access is possible.
|
||||||
|
For DVD-RAM and DVD+RW write addresses and data size need to be aligned
|
||||||
|
to 2 KiB. For DVD-RW alignment has to be 32 KiB. Within these limitations
|
||||||
|
the write address is at the discretion of the sending program.
|
||||||
|
Just use 2Ah WRITE to send data.
|
||||||
|
(mmc5r03c.pdf, 6.44)
|
||||||
|
When writing is done, it is mandatory to force the drive's buffer to media by
|
||||||
|
35h SYNCHRONIZE CACHE.
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
The size of the formatted area can be inquired by 23h READ FORMAT CAPACITIES.
|
||||||
|
The Number Of Blocks value in the Current/Maximum Capacity Descriptor gives
|
||||||
|
this size in 2 KiB blocks. But this is true only if Descriptor Type is 10b
|
||||||
|
("Formatted Media").
|
||||||
|
(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.2.3)
|
||||||
|
|
||||||
|
Not yet formatted areas may be completely forbidden or they may be allowed for
|
||||||
|
sequential writing (DVD-RW Intermediate state) or they may be allowed for
|
||||||
|
random access only after the necessary waiting time for formatting to reach
|
||||||
|
the desired address (DVD+RW with background formatting active).
|
||||||
|
|
||||||
|
Already written areas can be overwritten without special precaution.
|
||||||
|
Blanking a DVD-RW actually destroys its formatting.
|
||||||
|
|
||||||
|
|
||||||
|
Most of the concepts usually expressed in Write Parameters mode page 05h do not
|
||||||
|
apply to the recipes here: Test-Write, Buffer Underrun protection,
|
||||||
|
Multi-session, Write Type, Block Type, Track Mode, ...
|
||||||
|
There are hints for multi-session formats with DVD-RW but both of my drives
|
||||||
|
do not offer "Add Session" Format Types 12h or 14h.
|
||||||
|
(mmc5r03c.pdf 6.5.4.2.7 , 6.5.4.2.9)
|
||||||
|
|
||||||
|
|
||||||
|
Caution: Drive and media compatibility seems still to be quite an adventure.
|
||||||
|
If you experience problems, especially problems with readability, then try
|
||||||
|
different drives and media brands. Failure does not necessarily mean that the
|
||||||
|
software did anything wrong.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
DVD-RAM, fully formatted DVD+RW or DVD-RW :
|
||||||
|
|
||||||
|
Full format is the natural state of DVD-RAM.
|
||||||
|
|
||||||
|
DVD+RW reaches this state if Background Formatting is allowed to finish without
|
||||||
|
being stopped by 5Bh CLOSE TRACK SESSION.
|
||||||
|
(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h)
|
||||||
|
The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION
|
||||||
|
requesting Data Type 000b "Standard Disc Information". In the reply,
|
||||||
|
BG Format 3 indicates fully formatted media.
|
||||||
|
(mmc5r03c.pdf 6.22.3.1.13)
|
||||||
|
|
||||||
|
DVD-RW reaches this state either by Format Type 00h (or 10h) with maximum
|
||||||
|
size given as Number Of Blocks, or by writing sequentially until the disc is
|
||||||
|
completely full into an intermediate session opened by format 15h resp. 13h.
|
||||||
|
(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.1, 6.5.4.2.10, 6.5.4.2.8)
|
||||||
|
A fully formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The
|
||||||
|
Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted
|
||||||
|
Media") and 0 blocks are offered with Format Types 13h or 11h.
|
||||||
|
(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3)
|
||||||
|
See also discussion of unformatted or partially formatted DVD-RW below.
|
||||||
|
|
||||||
|
|
||||||
|
In fully formatted state there is no need for any formatting before writing nor
|
||||||
|
for any finalizing other than forcing the drive's buffer to media by
|
||||||
|
35h SYNCHRONIZE CACHE (which is mandatory for writing, anyway).
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
(It seems to do no harm to send to DVD+RW or DVD-RW a 5Bh CLOSE TRACK SESSION
|
||||||
|
with Close Function 010b despite there is no session open in this scenario.)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Unformatted DVD+RW
|
||||||
|
|
||||||
|
This is the state of previously unused DVD+RW media.
|
||||||
|
|
||||||
|
The formatting state of a DVD+RW may be inquired by 51h READ DISC INFORMATION
|
||||||
|
requiring Data Type 000b "Standard Disc Information".
|
||||||
|
In the reply, BG Format 0 indicates unformatted media (or unsuitable media).
|
||||||
|
(mmc5r03c.pdf 6.22.3.1.13)
|
||||||
|
|
||||||
|
Formatting has to be started by command 04h FORMAT UNIT, Format Type 26h.
|
||||||
|
Different from other format types, 26h allows to send a fantasy size of
|
||||||
|
0xffffffff blocks and does not require the caller to know the exact maximum
|
||||||
|
size offered with that format.
|
||||||
|
(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h)
|
||||||
|
|
||||||
|
As its name suggests, one has not to wait for background formatting to end
|
||||||
|
but may very soon start writing as on formatted media. Random access to
|
||||||
|
yet unformatted areas can last long, though.
|
||||||
|
|
||||||
|
If backup formatting has been started at the beginning of the session, then
|
||||||
|
it may get stopped after the final cache sync by 5Bh CLOSE TRACK SESSION
|
||||||
|
with Close Function 010b.
|
||||||
|
(mmc5r03c.pdf 6.3.3.6)
|
||||||
|
|
||||||
|
Formatting of DVD+RW is called "de-icing" because unformatted areas offer
|
||||||
|
no hold for random access addressing and are thus slippery like ice. One can
|
||||||
|
also see a color change from shiny unformatted to more dull formatted media.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Partly formatted DVD+RW :
|
||||||
|
|
||||||
|
This state is achieved by stopping background formatting before the media
|
||||||
|
was completely formmatted.
|
||||||
|
|
||||||
|
The formatting state of a DVD+RW is obtained by 51h READ DISC INFORMATION
|
||||||
|
requiring Data Type 000b "Standard Disc Information".
|
||||||
|
In the reply, BG Format 1 indicates partly formatted media.
|
||||||
|
(mmc5r03c.pdf 6.22.3.1.13)
|
||||||
|
|
||||||
|
If the data of the session surely fit into the formatted area, then it would
|
||||||
|
be unnecessary to restart background formatting.
|
||||||
|
But in order to make the DVD+RW surely accept its maximum number of bytes,
|
||||||
|
formatting may be restarted by command 04h FORMAT UNIT, Format Type 26h,
|
||||||
|
with the Restart bit set and Number of Blocks set to 0xffffffff.
|
||||||
|
(mmc5r03c.pdf, 6.5 FORMAT UNIT, 6.5.4.2.14 Format Type = 26h)
|
||||||
|
|
||||||
|
From then on, the same rules apply as for previously unformatted DVD+RW.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Unformatted DVD-RW (media profile is 0014h) :
|
||||||
|
|
||||||
|
This state is present with previously unused media. It is also present with
|
||||||
|
media blanked by programs cdrecord, wodim or dvd+rw-format and with media which
|
||||||
|
were sequentially written from blank state.
|
||||||
|
Profile transition from formatted 0013h to unformatted 0014h is done by
|
||||||
|
A1h BLANK.
|
||||||
|
(mmc5r03c.pdf, 6.2)
|
||||||
|
Experiments on my drives indicate that only Blanking Type 000b "Blank the disc"
|
||||||
|
achieves neat media. Media blanked via type 001b offer no feature 0021h and
|
||||||
|
stall cdrecord or libburn already when those media get examined. growisofs can
|
||||||
|
burn them - but only via DAO (feature 002Fh which prescribes Write Type 2).
|
||||||
|
(mmc5r03c.pdf 5.3.11, 5.3.25)
|
||||||
|
|
||||||
|
For becoming overwriteable such media need to be treated by command 04h FORMAT
|
||||||
|
UNIT.
|
||||||
|
(mmc5r03c.pdf, 6.5)
|
||||||
|
The Format Type has to be chosen from the list replied by 23h READ FORMAT
|
||||||
|
CAPACITIES. Suitable are Format Types 00h, 10h, 15h.
|
||||||
|
(mmc5r03c.pdf 6.24)
|
||||||
|
|
||||||
|
Format Types 00h and 10h provide a writeable area of a size given by Number of
|
||||||
|
Blocks. Type 00h seems to be the most traditional and complete one. It needs
|
||||||
|
no closing of a session at the end of writing.
|
||||||
|
The Number Of Blocks may be at most the value reported by 23h READ FORMAT
|
||||||
|
CAPACITIES in the entry for the desired format type. Full format is achieved
|
||||||
|
by sending exactly the reported value.
|
||||||
|
(mmc5r03c.pdf, 6.5.4.2.1 Format Type = 00h, 6.5.4.2.5 Format Type = 10h)
|
||||||
|
|
||||||
|
Format Type 15h provides a writeable area of given size too, but this area can
|
||||||
|
be expanded by sequential writing and afterwards marked as overwriteable by
|
||||||
|
closing the session. It is even allowed to format with size 0 and to leave
|
||||||
|
the size claim entirely to a sequential write process beginning at LBA 0.
|
||||||
|
(mmc5r03c.pdf, 6.5.4.2.10 Format Type = 15h)
|
||||||
|
When writing is done and cache is synced, one should send 5Bh CLOSE TRACK
|
||||||
|
SESSION with Close Function 010b in order to bring the session out of
|
||||||
|
Intermediate state.
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.3)
|
||||||
|
If not written up to the last 32 KiB block, the DVD-RW is only partly formatted
|
||||||
|
after that.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Partly formatted DVD-RW (media profile is 0013h) :
|
||||||
|
|
||||||
|
This state is achieved by formatting a DVD-RW with a number of blocks which
|
||||||
|
is less than offered for the Format Type by the drive's reply to 23h READ
|
||||||
|
FORMAT CAPACITIES. If the media was most recently formatted by Format Types
|
||||||
|
015h or 013h then it must have got written some bytes and afterwards treated
|
||||||
|
by 5Bh CLOSE TRACK SESSION, 010b in order to be partly formatted.
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.3 CLOSE TRACK SESSION 010b, 6.24 READ FORMAT CAPACITIES)
|
||||||
|
Elsewise the media is in Intermediate state. See below.
|
||||||
|
|
||||||
|
A partly formatted DVD-RW can be recognized by 23h READ FORMAT CAPACITIES. The
|
||||||
|
Descriptor Type of the Current/Maximum Capacity Descriptor is 10b ("Formatted
|
||||||
|
Media") and the Number Of Blocks with formats 00h, 10h or 15h is larger than the
|
||||||
|
currently formatted size, resp. more than 0 blocks are offered with Format
|
||||||
|
Types 13h or 11h.
|
||||||
|
(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3)
|
||||||
|
|
||||||
|
If the data of the session surely fit into the formatted area, then it would
|
||||||
|
be unnecessary to do any further formatting.
|
||||||
|
But in order to make the DVD-RW surely accept its maximum number of bytes,
|
||||||
|
partial formatting may be expanded by command 04h FORMAT UNIT, Format Type 13h,
|
||||||
|
which is supposed to be offered by the drive in this state. This brings the
|
||||||
|
session again into Intermediate state and thus allows expansion by sequential
|
||||||
|
writing. As with Format Type 15h it is ok to set Number Of Blocks to 0, so that
|
||||||
|
no fixed size formatting work is done and writing can begin soon after.
|
||||||
|
(mmc5r03c.pdf, 6.5.4.2.8 Format Type = 13h)
|
||||||
|
|
||||||
|
When writing is done and cache is synced, one should send 5Bh CLOSE TRACK
|
||||||
|
SESSION with Close Function 010b in order to bring the session out of
|
||||||
|
Intermediate state.
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.3)
|
||||||
|
If not written up to the last 32 KiB block, the DVD-RW is only partly formatted
|
||||||
|
after that.
|
||||||
|
|
||||||
|
Format Type 13h has been tested only with expanding sessions formatted by 15h.
|
||||||
|
Nevertheless it is offered with sessions from 00h and 10h, too.
|
||||||
|
According to the specs, Format Type 11h would expand a session by a fixed
|
||||||
|
size. This has not been tested yet because it is less appealing than 13h.
|
||||||
|
(mmc5r03c.pdf, 6.5.4.2.6 Format Type = 11h)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Intermediate state DVD-RW (media profile is 0013h) :
|
||||||
|
|
||||||
|
This state is achieved by formatting a DVD-RW with Format Type 15h or 13h
|
||||||
|
without subsequentially writing data and sending 5Bh CLOSE TRACK SESSION
|
||||||
|
with Close Function 010b.
|
||||||
|
Such media behave very unpleasing with my DVD-ROM drive under Linux 2.4 ide-cd.
|
||||||
|
One should therefore better avoid to release media in this state.
|
||||||
|
|
||||||
|
This state can be recognized by 23h READ FORMAT CAPACITIES. The Descriptor Type
|
||||||
|
of the Current/Maximum Capacity Descriptor is 11b ("Unknown Capacity") and
|
||||||
|
no formats 13h or 11h are offered.
|
||||||
|
(mmc5r03c.pdf, 6.24.3.2.1, 6.24.3.3)
|
||||||
|
|
||||||
|
One may treat such media as if Format Type 15h or 13h had been freshly applied.
|
||||||
|
I.e. sequential writing from LBA 0. After cache sync bring the session out
|
||||||
|
of Intermediate state by 5Bh CLOSE TRACK SESSION with Close Function 010b.
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.3)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Sequential DVD-R[W] Cookbook
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Inspired by Andy Polyakov's http://fy.chalmers.se/~appro/linux/DVD+RW/tools ,
|
||||||
|
backed by reading mmc5r03c.pdf from http://www.t10.org/ftp/t10/drafts/mmc5/
|
||||||
|
and by experiments with drives NEC ND-4570A and LG GSA-4082B.
|
||||||
|
|
||||||
|
For libburnia.pykix.org by Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Media type can be recognized by Current Profile from 46h GET CONFIGURATION.
|
||||||
|
(mmc5r03c.pdf 6.6.2.1)
|
||||||
|
DVD-R 0011h
|
||||||
|
DVD-RW Restricted Overwrite 0013h
|
||||||
|
DVD-RW Sequential Recording 0014h
|
||||||
|
|
||||||
|
There are two approaches for writing to sequential DVD-R[W]: DAO and
|
||||||
|
Incremental. Not all media and drives offer Incremental which allows
|
||||||
|
multi-session as with CD media and does not demand a predicted track size.
|
||||||
|
DAO seems to be the older method. It allows only one single session and
|
||||||
|
track and it demands an exactly predicted track size.
|
||||||
|
|
||||||
|
- About overwriteable, blank, appendable and finalized DVD-R[W] media
|
||||||
|
- Incremental writing
|
||||||
|
- DAO writing
|
||||||
|
- Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems
|
||||||
|
- Obtaining a Table Of Content from DVD-R[W]
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
About overwriteable, blank, appendable and finalized DVD-R[W] media :
|
||||||
|
|
||||||
|
DVD-RW can be either in formatted state Restricted Overwrite or in unformatted
|
||||||
|
state Sequential Recording. Sequential media can be either blank, appendable
|
||||||
|
or finalized.
|
||||||
|
|
||||||
|
Only blank and appendable media are sequentially writeable. For overwriteable
|
||||||
|
DVD-RW see the Overwriteable DVD Cookbook.
|
||||||
|
|
||||||
|
Overwriteable DVD-RW can be detected by their profile number 0013h in contrast
|
||||||
|
to profile number 0014h for sequential DVD-RW.
|
||||||
|
The status of sequential media can be inquired like with CD by 51h READ DISC
|
||||||
|
INFORMATION requesting Data Type 000b Standard Disc Information, where reply
|
||||||
|
value Disc Status indicates:
|
||||||
|
00b blank
|
||||||
|
01b appendable
|
||||||
|
10b finalized
|
||||||
|
11b others (unsuitable for this recipe)
|
||||||
|
(mmc5r03c.pdf 6.22.3.1.4)
|
||||||
|
|
||||||
|
Finalized, appendable or overwriteable DVD-RW can be brought into blank
|
||||||
|
sequential state by command A1h BLANK with blanking type 000b "Blank the disc".
|
||||||
|
See TAO Multi-Session CD Cookbook for details about blanking.
|
||||||
|
|
||||||
|
After minimal blanking (type 001b) DVD-RW my two drives do not offer the
|
||||||
|
Incremental Streaming feature 0021h the media any more. Full blanking (000b)
|
||||||
|
brings back this feature.
|
||||||
|
(mmc5r03c.pdf 6.2)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Incremental writing :
|
||||||
|
|
||||||
|
Incremental writing allows to produce multi-session DVDs. It is indicated
|
||||||
|
by feature 0021h being marked current in the reply of 46h GET CONFIGURATION.
|
||||||
|
growisofs inquires 0021h by setting Starting Feature Number to 0x21 and
|
||||||
|
Allocation Length to 16 in order to get only this one. The feature descriptor
|
||||||
|
begins at byte 8 of the reply. Its availability is indicated by the Current
|
||||||
|
Bit. libburn obtains the full feature list for this and other info.
|
||||||
|
(mmc5r03c.pdf 5.2.2. Feature Descriptor format, 5.3.11 Feature 0021h,
|
||||||
|
6.2 46h GET CONFIGURATION, )
|
||||||
|
In mode page 05h this method is selected by Write Type 00h.
|
||||||
|
|
||||||
|
Speed can be influenced by B6h SET STREAMING , speed capabilities can be
|
||||||
|
inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes
|
||||||
|
which are returned by ACh.
|
||||||
|
(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE)
|
||||||
|
|
||||||
|
growisofs fetches a mode page 05h template by MODE SENSE and inserts its own
|
||||||
|
parameters. It sets Multi-session to 11b, unless dvd_compat is nonzero.
|
||||||
|
libburn composes its mode page 05h from zero and allows control of
|
||||||
|
Multi-Session by the application.
|
||||||
|
BUFE Buffer Underrun protection 0=off, 1=on
|
||||||
|
LS_V Link size valid 1=true
|
||||||
|
Test Write -dummy mode for writing 0=off, 1=on
|
||||||
|
Write Type Packet/TAO/SAO/RAW 00h = Incremental (Packet)
|
||||||
|
Multi-session Wether to keep appendable 00b = finalize
|
||||||
|
11b = keep appendable
|
||||||
|
Track Mode Describes frame type 5 [*1]
|
||||||
|
Data Block Type Layout of payload blocks 8 [*2]
|
||||||
|
Link Size ??? 16 [*3]
|
||||||
|
FP Fixed Packet Size Bit 1
|
||||||
|
Packet Size 16 [*4]
|
||||||
|
(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode)
|
||||||
|
(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats)
|
||||||
|
[*1:]
|
||||||
|
growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number
|
||||||
|
Type 1, Track 1, Track Information Block byte 5 & 0xf.
|
||||||
|
(mmc5r03.pdf 6.27)
|
||||||
|
The specs predict that this will be Track Mode 4 (6.27.3.8) and also state that
|
||||||
|
default is 5 (7.5.4.12). 4 means: uninterrupted, do not copy. 5 means
|
||||||
|
increment, do not copy.
|
||||||
|
[*2:]
|
||||||
|
8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from
|
||||||
|
above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs
|
||||||
|
to be always true.
|
||||||
|
(mmc5r03.pdf 6.27.3.10)
|
||||||
|
[*3:]
|
||||||
|
growisofs (transport.hxx) sets Link Size to 16 for profiles 0011h and 0014h.
|
||||||
|
libburn now records the first link size from feature 0021h in its burn_drive
|
||||||
|
structure. If another link size item is 16, then 16 is used.
|
||||||
|
[*4:]
|
||||||
|
growisofs takes Packet Size from 52h. Specs predict it will be 16 (= 32 kB).
|
||||||
|
(mmc5r03.pdf 7.5.4.16)
|
||||||
|
|
||||||
|
The writing process is much like in "Writing a session to CD in TAO mode" :
|
||||||
|
Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION.
|
||||||
|
libburn writes full 32 kB buffers via 2Ah WRITE.
|
||||||
|
(mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE)
|
||||||
|
When writing is done, it is mandatory to force the drive's buffer to media by
|
||||||
|
35h SYNCHRONIZE CACHE.
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
The track has to be closed by 5Bh CLOSE TRACK SESSION Close Function 001b.
|
||||||
|
growisofs uses the logical track number for that and not FFh like libburn
|
||||||
|
does with TAO CD. So libburn obtains the Last Track Number in Last Session
|
||||||
|
from the reply of 51h READ DISC INFORMATION requesting Data Type 000b
|
||||||
|
"Standard Disc Information".
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.2 CLOSE TRACK, 6.22.3.1.)
|
||||||
|
|
||||||
|
Multiple tracks are permissible in a single session. After all of them have
|
||||||
|
been written, 5Bh CLOSE TRACK SESSION Close Function 010b with Logical Track
|
||||||
|
Number 0 closes the session. It depends on the Multi-Session value in mode
|
||||||
|
page 05h wether the disc is finalized or stays appendable.
|
||||||
|
(mmc5r03c.pdf 6.3.3.2.3)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
DAO writing :
|
||||||
|
|
||||||
|
DAO is the mode described by feature 002Fh. This feature also gives information
|
||||||
|
about capabilities for Burnfree (BUF), Test Write and DVD-RW.
|
||||||
|
(mmc5r03c.pdf 5.3.25)
|
||||||
|
Experiments with growisofs showed that the track size needs to be predicted
|
||||||
|
and may not be exceeded during the write process. (growisofs ran into SCSI
|
||||||
|
errors with piped non-ISO-9660 images and with piped ISO-9660 which have
|
||||||
|
trailing data.)
|
||||||
|
|
||||||
|
Speed can be influenced by B6h SET STREAMING , speed capabilities can be
|
||||||
|
inquired by ACh GET PERFORMANCE. It is advised to set only speeds and sizes
|
||||||
|
which are returned by ACh.
|
||||||
|
(mmc5r03c.pdf 6.39 SET STREAMING, 6.8 GET PERFORMANCE)
|
||||||
|
|
||||||
|
The mode page 05h to be sent :
|
||||||
|
BUFE Buffer Underrun protection 0=off, 1=on
|
||||||
|
LS_V Link size valid 0=false [*3]
|
||||||
|
Test Write -dummy mode for writing 0=off, 1=on
|
||||||
|
Write Type Packet/TAO/SAO/RAW 02h = DAO (same code as SAO)
|
||||||
|
Multi-session Wether to keep appendable 00b = finalize
|
||||||
|
Track Mode Describes frame type 5 [*1]
|
||||||
|
Data Block Type Layout of payload blocks 8 [*2]
|
||||||
|
Link Size ??? 0 [*3]
|
||||||
|
FP Fixed Packet Size Bit 0 [*3]
|
||||||
|
Packet Size 0 [*3]
|
||||||
|
(mmc5r03c.pdf 7.5.4 The Mode Page, 4.2.3.4 Table 17 CONTROL = Track Mode)
|
||||||
|
(spc3r23.pdf 6.8 MODE SELECT, 7.4.3 Mode parameter header formats)
|
||||||
|
[*1:]
|
||||||
|
growisofs takes the Track Mode from 52h READ TRACK INFORMATION, Address/Number
|
||||||
|
Type 1, Track 1, Track Information Block byte 5 & 0xf.
|
||||||
|
(mmc5r03.pdf 6.27)
|
||||||
|
[*2:]
|
||||||
|
8 means: 2048 byte data blocks. growisofs sets this value if Data Mode from
|
||||||
|
above 52h READ TRACK INFORMATION is 1 or Fh, which is predicted by the specs
|
||||||
|
to be always true. (If not: growisofs aborts.)
|
||||||
|
(mmc5r03.pdf 6.27.3.10)
|
||||||
|
[*3:]
|
||||||
|
Link Size, Packet Size and their companions only apply to Write Type 00h.
|
||||||
|
|
||||||
|
The session layout must be described by 53h RESERVE TRACK, RMZ=ARSV=0.
|
||||||
|
Reservation size should better already be aligned to 32 KiB. It has not been
|
||||||
|
tested yet, what happens if not enough data get written.
|
||||||
|
(mmc5r03c.pdf 6.31)
|
||||||
|
|
||||||
|
Next Writeable Address is fetched from the reply of 52h READ TRACK INFORMATION.
|
||||||
|
The reply is supposed to be 0. libburn writes full 32 kB buffers via 2Ah WRITE.
|
||||||
|
(mmc5r03c.pdf, 6.27 READ TRACK INFORMATION, 6.44 WRITE)
|
||||||
|
If the track source delivers less than the announced size then libburn pads up
|
||||||
|
by zeros.
|
||||||
|
When writing is done, it is mandatory to force the drive's buffer to media by
|
||||||
|
35h SYNCHRONIZE CACHE.
|
||||||
|
(mmc5r03c.pdf, 6.41)
|
||||||
|
|
||||||
|
No further finalization is necessary. (I.e. no 5Bh CLOSE TRACK SESSION.)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Obtaining DVD-R[W] multi-session info for extending ISO-9660 filesystems :
|
||||||
|
|
||||||
|
Like with CD it is necessary to obtain the two numbers for mkisofs option -C
|
||||||
|
in order to prepare a ISO-9660 filesystem image which by its inner pointers
|
||||||
|
matches the block addresses of the future location on media.
|
||||||
|
These are the start address of the first track in the last complete session
|
||||||
|
and the predicted start address of the track which will host the new image.
|
||||||
|
See TAO Multi-Session CD Cookbook for some more info about mkisofs aspects.
|
||||||
|
|
||||||
|
|
||||||
|
The first number may be gained by 43h READ TOC/PMA/ATIP Format 0001b which in
|
||||||
|
table 478 promises quick access via Start Address Of First Track In Last
|
||||||
|
Session.
|
||||||
|
(mmc5r03c.pdf 6.26.2.5 table 478, 6.26.3.3.1)
|
||||||
|
Regrettably the MMC-5 specs still define a useless reply for non-CD media
|
||||||
|
which obviously stems from MMC-3 times when no multi-session was possible
|
||||||
|
with non-CD.
|
||||||
|
(mmc5r03c.pdf 6.26.3.3.3)
|
||||||
|
Both my drives do give a useful reply with the correct number for appendable
|
||||||
|
DVD-RW. But not being backed by the specs this method appears unappealing .
|
||||||
|
|
||||||
|
Another approach would be a formatted Table of Content, obtained by 43h READ
|
||||||
|
TOC/PMA/ATIP Format 0000b. The specs do not totally outrule that this returns
|
||||||
|
useful data with non-CD but they define a crippled TOC for multi-session.
|
||||||
|
(mmc5r03c.pdf 6.26.3.2.4)
|
||||||
|
My LG drive returns a more detailed TOC, my NEC drive stays with the rather
|
||||||
|
suboptimal specs. So one would get different TOCs on different drives.
|
||||||
|
Nevertheless, the MMC-5 compliant TOC would return the desired number in
|
||||||
|
the Track Start address of the track with the highest number before AAh.
|
||||||
|
|
||||||
|
Most stable seems the approach to obtain the desired number from the reply
|
||||||
|
of 52h READ TRACK INFORMATION, Address/Number Type 01b. The field Logical Block
|
||||||
|
Address/Track/Session has to bear the track number of the first track in the
|
||||||
|
last complete session. To determine this number one has to determine the
|
||||||
|
number of the last session and the number of the last track from 51h READ DISC
|
||||||
|
INFORMATION and to iterate over the tracknumber by 52h READ TRACK INFORMATION
|
||||||
|
until the first track with the desired session number appears and reveils
|
||||||
|
its start address.
|
||||||
|
(mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK)
|
||||||
|
This method is very near to fabricating an own TOC. So libburn does this
|
||||||
|
when inspecting the media. If the first number for -C is needed, libburn
|
||||||
|
inquires its TOC model for the address of the first track in the last
|
||||||
|
complete session. See below for a detailed description of TOC fabrication.
|
||||||
|
|
||||||
|
|
||||||
|
The second -C number is the exact prediction of future track start address. It
|
||||||
|
is gained like with CD by 52h READ TRACK INFORMATION Type 01b. Different from
|
||||||
|
CD one may not use track number FFh but has to use the Last Track in Last
|
||||||
|
Session from 51h READ DISC INFORMATION.
|
||||||
|
(mmc5r03c.pdf 6.22 51h DISC, 6.27 52h TRACK)
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
Obtaining a Table Of Content from DVD-R[W] :
|
||||||
|
|
||||||
|
The raw TOC entries from 43h READ TOC/PMA/ATIP Format 0010b as described with
|
||||||
|
CD media are not available with non-CD.
|
||||||
|
There is a Format 0000b "Formatted TOC" but this is with non-CD a fictional
|
||||||
|
information much at the discretion of the drive. Two drives with the same disc
|
||||||
|
may well return different Formatted TOC. They are supposed to be consistent
|
||||||
|
only about the last complete session and even there the MMC-5 specification
|
||||||
|
6.26.3.2.5 seems to prescribe a structure which does not match the true
|
||||||
|
structure of incremental writing to sequential DVD-R[W].
|
||||||
|
(mmc5r03c.pdf 6.26.3.2)
|
||||||
|
So i prefer not to use this method of getting a TOC.
|
||||||
|
|
||||||
|
|
||||||
|
The alternative is to produce an own TOC from information gained by 51h READ
|
||||||
|
DISC INFORMATION and by 52h READ TRACK INFORMATION which reveil a CD-like
|
||||||
|
structure of sessions and 1:n related tracks.
|
||||||
|
|
||||||
|
51h READ DISC INFORMATION Data Type 000b, fields Number of Sessions (Least
|
||||||
|
Significant Byte) and Number of Sessions (Most Significant Byte) give the
|
||||||
|
number of sessions. The last complete session number of an appendable disc
|
||||||
|
is one less because there is an incomplete session at its end. libburn only
|
||||||
|
records complete sessions in its TOC model.
|
||||||
|
libburn uses Last Track in Last Session as a hint for the range of track
|
||||||
|
numbers.
|
||||||
|
(mmc5r03c.pdf 6.22)
|
||||||
|
|
||||||
|
Next step is to iterate from 1 up to the last track number and to obtain
|
||||||
|
the according track info by 52h READ TRACK INFORMATION. Each track tells its
|
||||||
|
Session Number (LSB at byte 2, MSB at 33), its Logical Track Start Address,
|
||||||
|
its Logical Track Size, and much more which is not needed for a fake CD TOC.
|
||||||
|
One may analyze the track info more finely but for this special purpose
|
||||||
|
it is enough to discard the tracks which do not belong to complete sessions.
|
||||||
|
(mmc5r03c.pdf 6.27)
|
||||||
|
|
||||||
|
At the end of each session libburn inserts fake leadout entries into its TOC
|
||||||
|
model. Their start address is computed from the start and size of the last
|
||||||
|
track of the session.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
@ -56,8 +56,8 @@ WARN_LOGFILE =
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# configuration options related to the input files
|
# configuration options related to the input files
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
INPUT = libburn libisofs doc test
|
INPUT = libburn doc test
|
||||||
FILE_PATTERNS = libburn.h libisofs.h comments libburner.c
|
FILE_PATTERNS = libburn.h comments libburner.c
|
||||||
RECURSIVE = NO
|
RECURSIVE = NO
|
||||||
EXCLUDE =
|
EXCLUDE =
|
||||||
EXCLUDE_SYMLINKS = NO
|
EXCLUDE_SYMLINKS = NO
|
||||||
|
@ -7,5 +7,6 @@ Name: libburn
|
|||||||
Description: Disc reading/writing library
|
Description: Disc reading/writing library
|
||||||
Version: @VERSION@
|
Version: @VERSION@
|
||||||
Requires:
|
Requires:
|
||||||
Libs: -L${libdir} -lburn @THREAD_LIBS@
|
Libs: -L${libdir} -lburn
|
||||||
|
Libs.private: @THREAD_LIBS@ @LIBBURN_ARCH_LIBS@
|
||||||
Cflags: -I${includedir}/libburn
|
Cflags: -I${includedir}/libburn
|
||||||
|
792
libburn/asserts.txt
Normal file
792
libburn/asserts.txt
Normal file
@ -0,0 +1,792 @@
|
|||||||
|
List of assert() calls in libburn. 6 Oct 2006.
|
||||||
|
|
||||||
|
Format:
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Number) grep'ed line
|
||||||
|
(++ before number means: is fully done, + means is done so far )
|
||||||
|
function():
|
||||||
|
Description of abort condition.
|
||||||
|
|
||||||
|
Possible callers and their relation to the abort condition.
|
||||||
|
|
||||||
|
: Error Evaluation
|
||||||
|
=> Consequences
|
||||||
|
|
||||||
|
Eventual implementation timestamp
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 1) libburn/async.c: assert(a != NULL); /* wasn't found.. this should not be possible */
|
||||||
|
static remove_worker():
|
||||||
|
A thread describing structure (struct w_list) could not be found in
|
||||||
|
order to be released.
|
||||||
|
|
||||||
|
Called by API burn_drive_scan()
|
||||||
|
Called by static erase_worker_func() , thread under API burn_disc_erase()
|
||||||
|
Called by static write_disc_worker_func(), thread under API burn_disc_write()
|
||||||
|
All three want to clean up after they are done.
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> issue LIBDAX_MSGS_SEV_WARNING
|
||||||
|
|
||||||
|
ts A61006
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 2) libburn/async.c: assert(!(workers && workers->drive));
|
||||||
|
API burn_drive_scan():
|
||||||
|
Before spawning a thread, the function refuses work because another
|
||||||
|
drive activity is going on.
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
|
||||||
|
ts A61006
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
+ 3) libburn/async.c: assert(workers == NULL);
|
||||||
|
API burn_drive_scan():
|
||||||
|
After thread is done and remover_worker() succeeded, there is still a
|
||||||
|
worker registered. Shall probably detect roguely appeared burn or
|
||||||
|
erase runs. (I consider to install a mutex shielded function for that.)
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> Same as 1)
|
||||||
|
|
||||||
|
ts A61006
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 4) libburn/async.c: assert(drive);
|
||||||
|
libburn/async.c: assert(!SCAN_GOING());
|
||||||
|
libburn/async.c: assert(!find_worker(drive));
|
||||||
|
API burn_disc_erase():
|
||||||
|
Wants to see a drive (assumes NULL == 0), wants to see no scan and
|
||||||
|
wants to see no other worker on that drive. I.e. this would tolerate
|
||||||
|
a parallel activity on another drive.
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> (no return value), issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
|
||||||
|
ts A61006
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 5) libburn/async.c: assert(!SCAN_GOING());
|
||||||
|
libburn/async.c: assert(!find_worker(opts->drive));
|
||||||
|
API burn_disc_write():
|
||||||
|
Same as 4)
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> Same as 4)
|
||||||
|
|
||||||
|
ts A61006
|
||||||
|
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 6) libburn/drive.c: assert(d->busy == BURN_DRIVE_IDLE);
|
||||||
|
API burn_drive_release():
|
||||||
|
A drive is not idle on release.
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> Same as 4)
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 7) libburn/drive.c: assert(d->released);
|
||||||
|
burn_wait_all()
|
||||||
|
A drive is found grabbed.
|
||||||
|
|
||||||
|
Called by burn_drive_scan_sync(), thread under API burn_drive_scan()
|
||||||
|
Called by API burn_finish
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> rename and redefine burn_wait_all() : now burn_drives_are_clear()
|
||||||
|
=> change all use of burn_wait_all()
|
||||||
|
=> Move tests up to burn_drive_scan()
|
||||||
|
=> There: return -1; issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 8) libburn/drive.c: assert(!d->released);
|
||||||
|
API burn_disc_get_status()
|
||||||
|
Attempt to read status of non-grabbed drive.
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> extend enum burn_disc_status by BURN_DISC_UNGRABBED
|
||||||
|
=> return BURN_DISC_UNGRABBED, issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 9) libburn/drive.c: assert( /* (write_type >= BURN_WRITE_PACKET) && */
|
||||||
|
burn_drive_get_block_types():
|
||||||
|
Will not work on BURN_WRITE below BURN_WRITE_RAW.
|
||||||
|
|
||||||
|
Called by -nobody- ?
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> inactivate unused function
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 10) libburn/drive.c: assert(d->idata);
|
||||||
|
libburn/drive.c: assert(d->mdata);
|
||||||
|
static drive_getcaps():
|
||||||
|
sg.c:enumerate_common() did not succeed in creating a proper struct burn_drive
|
||||||
|
Called by burn_drive_scan_sync()
|
||||||
|
|
||||||
|
: Severe System Error
|
||||||
|
=> This could possibly really stay an abort() because the reason is
|
||||||
|
a plain failure of the system's memory management.
|
||||||
|
=> Detect this failure already in enumerate_common(),
|
||||||
|
issue LIBDAX_MSGS_SEV_FATAL, return
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 11) libburn/drive.c: assert(burn_running);
|
||||||
|
burn_drive_scan_sync():
|
||||||
|
The library was not initialized.
|
||||||
|
|
||||||
|
Called as thread by API burn_drive_scan()
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> Move this test up to burn_drive_scan()
|
||||||
|
=> There: return -1; redefine @return in API , issue LIBDAX_MSGS_SEV_FATAL
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 12) libburn/drive.c: assert(d->released == 1);
|
||||||
|
burn_drive_scan_sync():
|
||||||
|
Inactivated
|
||||||
|
|
||||||
|
: (Severe Application Error)
|
||||||
|
=> throw out inactivated code
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 13) libburn/drive.c: assert(strlen(d->devname) < BURN_DRIVE_ADR_LEN);
|
||||||
|
burn_drive_raw_get_adr():
|
||||||
|
An enumerated device address is longer than the API's maximum length
|
||||||
|
|
||||||
|
Called by API burn_drive_get_adr()
|
||||||
|
Called by API burn_drive_obtain_scsi_adr()
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> return -1; in all three functions, enhance burn_drive_get_adr @return docs
|
||||||
|
=> issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 14) libburn/drive.c: assert(drive_info->drive!=NULL);
|
||||||
|
API burn_drive_get_adr():
|
||||||
|
Drive info has no drive attached.
|
||||||
|
|
||||||
|
: Severe Libburn Error (unlikely, will eventually SIGSEGV on NULL)
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 15) libburn/init.c: assert(burn_running);
|
||||||
|
API burn_finish():
|
||||||
|
The library is not initialized
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> return (assume no msg system)
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 16) libburn/init.c: assert(burn_running);
|
||||||
|
API burn_preset_device_open():
|
||||||
|
The library is not initialized
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> return (assume no msg system)
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 17) libburn/mmc.c: assert(o->drive == d);
|
||||||
|
mmc_close_disc():
|
||||||
|
alias: struct burn_drive.close_disc()
|
||||||
|
Parameters struct burn_drive and struct burn_write_opts do not match
|
||||||
|
|
||||||
|
Called by -nobody- ?
|
||||||
|
|
||||||
|
( => Disable unused function ? )
|
||||||
|
=> removed redundant parameter struct burn_drive
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 18) libburn/mmc.c: assert(o->drive == d);
|
||||||
|
mmc_close_session():
|
||||||
|
Same as 17)
|
||||||
|
alias: struct burn_drive.close_session()
|
||||||
|
|
||||||
|
Called by -nobody- ?
|
||||||
|
|
||||||
|
( => Disable unused function ? )
|
||||||
|
=> removed redundant parameter struct burn_drive
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 19) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||||
|
mmc_write_12():
|
||||||
|
- Unclear what .bytes and .sectors mean in struct buffer -
|
||||||
|
|
||||||
|
Called by -nobody- ?
|
||||||
|
|
||||||
|
=> problems with filling the write buffer have to be handled by callers
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 20) libburn/mmc.c: assert(buf->bytes >= buf->sectors); /* can be == at 0... */
|
||||||
|
mmc_write():
|
||||||
|
- Unclear what .bytes and .sectors mean in struct buffer -
|
||||||
|
|
||||||
|
libburn/mmc.c: c.page->sectors = errorblock - start + 1;
|
||||||
|
mmc_read_sectors() by toc_find_modes() by mmc_read_toc() alias drive.read_toc()
|
||||||
|
by burn_drive_grab()
|
||||||
|
This seems to be unrelated to mmc_write().
|
||||||
|
|
||||||
|
libburn/sector.c: out->sectors++;
|
||||||
|
get_sector()
|
||||||
|
Seems to hand out sector start pointer in opts->drive->buffer
|
||||||
|
and to count reservation transactions as well as reserved bytes.
|
||||||
|
Ensures out->bytes >= out->sectors
|
||||||
|
|
||||||
|
|
||||||
|
libburn/mmc.c: c.page->bytes = s->count * 8;
|
||||||
|
mmc_send_cue_sheet()
|
||||||
|
Does not use mmc_write() but directly (sg_)issue_command()
|
||||||
|
|
||||||
|
libburn/sector.c: out->bytes += seclen;
|
||||||
|
get_sector()
|
||||||
|
See above
|
||||||
|
Ensures out->bytes >= out->sectors
|
||||||
|
|
||||||
|
libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||||
|
spc_select_error_params()
|
||||||
|
Does not use mmc_write() but directly (sg_)issue_command()
|
||||||
|
|
||||||
|
libburn/spc.c: c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||||
|
spc_select_error_params()
|
||||||
|
Does not use mmc_write() but directly (sg_)issue_command()
|
||||||
|
|
||||||
|
libburn/spc.c: c.page->bytes = 8 + 2 + 0x32;
|
||||||
|
spc_probe_write_modes()
|
||||||
|
Does not use mmc_write() but directly (sg_)issue_command()
|
||||||
|
|
||||||
|
alias struct burn_drive.write()
|
||||||
|
Called by static get_sector, by many
|
||||||
|
Called by burn_write_flush
|
||||||
|
Called by burn_write_track
|
||||||
|
|
||||||
|
=> problems with filling the write buffer have to be handled by callers
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 21) libburn/mmc.c: assert(((dlen - 2) % 11) == 0);
|
||||||
|
mmc_read_toc():
|
||||||
|
- Is defunct -
|
||||||
|
|
||||||
|
=> :)
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 22) libburn/mmc.c: assert(len >= 0);
|
||||||
|
mmc_read_sectors():
|
||||||
|
Catches a bad parameter
|
||||||
|
|
||||||
|
alias: struct burn_drive.read_sectors()
|
||||||
|
Called by API burn_disc_read() , - is defunct -, one could catch the problem
|
||||||
|
Called by toc_find_modes(), problem cannot occur: mem.sectors = 1;
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
(=> in burn_disc_read() check page.sectors before d->read_sectors() )
|
||||||
|
=> :)
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 23) libburn/mmc.c: assert(d->busy);
|
||||||
|
mmc_read_sectors():
|
||||||
|
Catches use of a drive that is not marked as busy
|
||||||
|
|
||||||
|
alias: struct burn_drive.read_sectors()
|
||||||
|
Called by API burn_disc_read() , - is defunct -, busy = BURN_DRIVE_READING;
|
||||||
|
Called by toc_find_modes(), does the same assert. To be solved there.
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> :)
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 24) libburn/options.c: assert(0);
|
||||||
|
API burn_write_opts_set_write_type():
|
||||||
|
Detects unsuitable enum burn_write_types write_type and int block_type.
|
||||||
|
API promises return 0 on failure
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> issue LIBDAX_MSGS_SEV_SORRY
|
||||||
|
=> should also detect problem of 26) : wrong write_type,block_type combination
|
||||||
|
by calling sector_get_outmode() and checking for -1
|
||||||
|
=> should also detect problem of 41) : unknown block_type
|
||||||
|
by spc_block_type() and checking for -1
|
||||||
|
=> delete assert(0)
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 25) libburn/read.c: assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||||
|
libburn/read.c: assert(!d->busy);
|
||||||
|
libburn/read.c: assert(d->toc->valid);
|
||||||
|
libburn/read.c: assert(o->datafd != -1);
|
||||||
|
API burn_disc_read():
|
||||||
|
- ? -
|
||||||
|
|
||||||
|
burn_disc_read() is defunct
|
||||||
|
OPTIONS_VERSION does not occur outside this line
|
||||||
|
|
||||||
|
( => one would return )
|
||||||
|
( 22) => catch page.sectors<0 before d->read_sectors() )
|
||||||
|
( 37) => catch ! d->mdata->valid )
|
||||||
|
=> :)
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 26) libburn/sector.c: assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||||
|
static get_outmode():
|
||||||
|
burn_write_opts is wrongly programmed with .write_type and .block_type
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> This gets handled by burn_write_opts_set_write_type()
|
||||||
|
ts A61007 by new semi-public sector_get_outmode()
|
||||||
|
=> delete assert()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 27) libburn/sector.c: assert(outlen >= inlen);
|
||||||
|
libburn/sector.c: assert(outmode & BURN_MODE_RAW);
|
||||||
|
libburn/sector.c: assert(offset != -1);
|
||||||
|
static convert_data():
|
||||||
|
Several unacceptable settings within struct burn_write_opts
|
||||||
|
|
||||||
|
Called by sector_toc() sector_pregap() sector_postgap() sector_lout()
|
||||||
|
sector_data()
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> change return type of convert_data()
|
||||||
|
=> all callers interpret return value and eventually return failure
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 28) libburn/sector.c: assert(0);
|
||||||
|
static char_to_isrc():
|
||||||
|
Called by subcode_user() with data set by API burn_track_set_isrc()
|
||||||
|
Some character conversion fails on wrong input
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> burn_track_set_isrc() has to make sure that only good data are set
|
||||||
|
=> char_to_isrc() returns 0 as default
|
||||||
|
=> delete assert()
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 29) libburn/sector.c: assert(qmode == 1 || qmode == 2 || qmode == 3);
|
||||||
|
subcode_user():
|
||||||
|
- can not happen -
|
||||||
|
|
||||||
|
: Unknown reason of assert()
|
||||||
|
=> remove assert()
|
||||||
|
|
||||||
|
ts A61010
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 30) libburn/sector.c: assert(modebyte == 1);
|
||||||
|
sector_headers():
|
||||||
|
Does only accept modes BURN_AUDIO, BURN_MODE1 or write_type BURN_WRITE_SAO
|
||||||
|
|
||||||
|
Called by sector_toc() sector_pregap() sector_postgap() sector_lout()
|
||||||
|
sector_data()
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> new functions sector_headers_is_ok(), burn_disc_write_is_ok()
|
||||||
|
help to catch problem in API burn_disc_write()
|
||||||
|
=> issue LIBDAX_MSGS_SEV_FATAL
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 31) libburn/sector.c: assert(0);
|
||||||
|
process_q()
|
||||||
|
- defunct -
|
||||||
|
|
||||||
|
=> :)
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 32) libburn/sg.c: assert("drive busy" == "non fatal");
|
||||||
|
sg_handle_busy_device():
|
||||||
|
Intentional abort preset by the app
|
||||||
|
|
||||||
|
=> change to abort()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 33) libburn/sg.c: assert(fd != -1337);
|
||||||
|
sg_grab():
|
||||||
|
The drive device file could not be opened
|
||||||
|
|
||||||
|
:Severe External Problem
|
||||||
|
=> obsolete by normal drive open failure handling
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 34) libburn/sg.c: assert(!c->page);
|
||||||
|
sg_issue_command():
|
||||||
|
An SCSI command of direction NO_TRANSFER may not have a .page != NULL.
|
||||||
|
|
||||||
|
Since it is about exposing a libburn detail towards the sg driver, i believe
|
||||||
|
it is sufficient to just not use it.
|
||||||
|
|
||||||
|
: Libburn Error
|
||||||
|
=> enhance internal logics of sg_issue_command()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 35) libburn/sg.c: assert(c->page->bytes > 0);
|
||||||
|
sg_issue_command():
|
||||||
|
An SCSI command of direction TO_DRIVE wants to transfer 0 bytes.
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> set command.error = 1 and return 0
|
||||||
|
|
||||||
|
ts A61010
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 36) libburn/sg.c: assert(err != -1);
|
||||||
|
sg_issue_command():
|
||||||
|
The transfer of the command via ioctl() failed
|
||||||
|
|
||||||
|
: Severe Transport Level Problem
|
||||||
|
=> close drive fd, set idle and released
|
||||||
|
=> set command.error = 1 and return -1
|
||||||
|
|
||||||
|
ts A61010
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 37) libburn/spc.c: assert(d->mdata->valid);
|
||||||
|
spc_select_error_params():
|
||||||
|
Drive was not properly programmed
|
||||||
|
|
||||||
|
alias struct burn_drive.send_parameters()
|
||||||
|
Called by burn_disc_read, - defunct -
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> moved up as mangled assert to burn_disc_read()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 38) libburn/spc.c: assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||||
|
spc_sense_write_params():
|
||||||
|
Drive does not offer write of any known media type
|
||||||
|
|
||||||
|
alias struct burn_drive.read_disc_info()
|
||||||
|
Called by API burn_drive_grab (assert test made there in soft)
|
||||||
|
|
||||||
|
: Severe Command Level Problem
|
||||||
|
=> remove assert()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 39) libburn/spc.c: assert(o->drive == d);
|
||||||
|
spc_select_write_params():
|
||||||
|
Drive does not match struct burn_write_opts
|
||||||
|
|
||||||
|
alias struct burn_drive.send_write_parameters()
|
||||||
|
Called by mmc_close_disc() (-defunct- ?), mmc_close_session() (-defunct- ?),
|
||||||
|
burn_write_track() (d = o->drive;),
|
||||||
|
burn_disc_write_sync() d = (o->drive;)
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> remove assert()
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 40) libburn/spc.c: assert(d->mdata->valid);
|
||||||
|
spc_select_write_params():
|
||||||
|
Drive was not properly programmed
|
||||||
|
|
||||||
|
Called by (see 39)
|
||||||
|
burn_write_track() by burn_write_session() by burn_disc_write_sync()
|
||||||
|
burn_disc_write_sync() indirectly by API burn_disc_write()
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> caught in burn_disc_write() now
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 41) libburn/spc.c: assert(0);
|
||||||
|
spc_block_type():
|
||||||
|
Unknown value with enum burn_block_types
|
||||||
|
|
||||||
|
Called by spc_select_write_params, uses burn_write_opts.block_type,
|
||||||
|
set by API burn_write_opts_set_write_type()
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> catch in API burn_write_opts_set_write_type
|
||||||
|
by calling spc_block_type()
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61007
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 42) libburn/structure.c: assert(!(pos > BURN_POS_END));\
|
||||||
|
macro RESIZE
|
||||||
|
An illegal list index is given by the app.
|
||||||
|
|
||||||
|
( TO->NEW##s obviusly means to append "s" to cpp result of TO->NEW )
|
||||||
|
Used by API burn_session_add_track() and API burn_disc_add_session()
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> replace assert by if-and-return-0
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 43) libburn/structure.c: assert(s->track != NULL);
|
||||||
|
API burn_session_remove_track()
|
||||||
|
An application supplied pointer is NULL
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> replace by if-and-return-0
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 44) libburn/structure.c: assert((country[i] >= '0' || country[i] < '9') &&
|
||||||
|
libburn/structure.c: assert((owner[i] >= '0' || owner[i] < '9') &&
|
||||||
|
libburn/structure.c: assert(year <= 99);
|
||||||
|
libburn/structure.c: assert(serial <= 99999);
|
||||||
|
API burn_track_set_isrc():
|
||||||
|
Illegal texts supplied by application.
|
||||||
|
The logical expression is always true !
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> issue LIBDAX_MSGS_SEV_SORRY and return
|
||||||
|
=> delete assert
|
||||||
|
=> delete assert 28) in char_to_isrc()
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 45) libburn/toc.c: assert(0); /* unhandled! find out ccd's
|
||||||
|
static write_clonecd2():
|
||||||
|
|
||||||
|
- defunct -, - unused -
|
||||||
|
|
||||||
|
=> mangle assert
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 46) libburn/toc.c: assert(d->busy);
|
||||||
|
toc_find_modes():
|
||||||
|
The drive to work on is not marked busy
|
||||||
|
|
||||||
|
Called by mmc_read_toc() alias read_toc() by ... burn_drive_grab()
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> to be prevented on the higher levels
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 47) libburn/util.c: assert(s);
|
||||||
|
burn_strdup()
|
||||||
|
Abort on NULL string which would elsewise cause a SIGSEGV
|
||||||
|
|
||||||
|
Used once in enumerate_common() with a string that worked with open(2) before
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 48) libburn/util.c: assert(s);
|
||||||
|
burn_strndup(): - unused -
|
||||||
|
Same as 47
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> return NULL
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 49) libburn/util.c: assert(n > 0);
|
||||||
|
burn_strndup(): - unused -
|
||||||
|
Prevent problems by negative copy length
|
||||||
|
|
||||||
|
: Severe Libburn Error
|
||||||
|
=> return NULL
|
||||||
|
=> delete assert
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 50) libburn/write.c: assert(0);
|
||||||
|
static type_to_ctrl():
|
||||||
|
Unsuitable mode to be converted into "ctrl"
|
||||||
|
Called by static type_to_form() finally burn_create_toc_entries()
|
||||||
|
|
||||||
|
: Severe Application Error
|
||||||
|
=> to be caught in burn_track_define_data by calling for test type_to_form()
|
||||||
|
=> return -1;
|
||||||
|
|
||||||
|
ts A61008
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 51) libburn/write.c: assert(0);
|
||||||
|
libburn/write.c: assert(0); /* XXX someone's gonna want this sometime */
|
||||||
|
static type_to_form():
|
||||||
|
Does not like BURN_MODE0 or BURN_MODE2 but tolerates unknown modes
|
||||||
|
|
||||||
|
Called by static burn_create_toc_entries() by burn_disc_write_sync()
|
||||||
|
|
||||||
|
: Undocumented Libburn Restriction
|
||||||
|
=> set *form = -1 , *ctladr = 0xff , return
|
||||||
|
=> make function non-static
|
||||||
|
=> call for test in API burn_track_define_data()
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 52) libburn/write.c: assert(ptr);
|
||||||
|
static add_cue():
|
||||||
|
realloc() failed
|
||||||
|
|
||||||
|
Called by burn_create_toc_entries() by burn_disc_write_sync()
|
||||||
|
(burn_create_toc_entries is ignorant towards own potential memory problems)
|
||||||
|
(This could possibly really stay an abort() because the reason is
|
||||||
|
a plain failure of the system's memory management.)
|
||||||
|
|
||||||
|
: Severe System Error
|
||||||
|
=> change return type of add_cue to int
|
||||||
|
=> react on return -1 in burn_create_toc_entries, return NULL on failure
|
||||||
|
=> abort burn_disc_write_sync() on NULL return
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 53) libburn/write.c: assert(d->toc_entry == NULL);
|
||||||
|
burn_create_toc_entries():
|
||||||
|
Multiple usage of struct burn_drive.toc_entry
|
||||||
|
|
||||||
|
Called by burn_disc_write_sync()
|
||||||
|
This will probably trigger an abort with disc->sessions > 1
|
||||||
|
(disc->sessions is incremented in macro RESIZE() as "NEW##s")
|
||||||
|
|
||||||
|
: Design Problem
|
||||||
|
( => ? disallow multiple sessions ? )
|
||||||
|
=> replace assert by soft means and wait what happens
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
++ 54) libburn/write.c: assert(0);
|
||||||
|
burn_sector_length():
|
||||||
|
Only BURN_AUDIO, BURN_MODE_RAW, BURN_MODE1 are allowed
|
||||||
|
|
||||||
|
Called by get_sector(), convert_data(), ...
|
||||||
|
|
||||||
|
=> call burn_sector_length() for test in API burn_track_define_data()
|
||||||
|
=> replace assert by -1
|
||||||
|
|
||||||
|
ts A61009
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
221
libburn/async.c
221
libburn/async.c
@ -6,10 +6,18 @@
|
|||||||
#include "write.h"
|
#include "write.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "async.h"
|
#include "async.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "back_hacks.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <a ssert.h>
|
||||||
|
*/
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
#define SCAN_GOING() (workers && !workers->drive)
|
#define SCAN_GOING() (workers && !workers->drive)
|
||||||
|
|
||||||
@ -29,6 +37,14 @@ struct erase_opts
|
|||||||
int fast;
|
int fast;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ts A61230 */
|
||||||
|
struct format_opts
|
||||||
|
{
|
||||||
|
struct burn_drive *drive;
|
||||||
|
off_t size;
|
||||||
|
int flag;
|
||||||
|
};
|
||||||
|
|
||||||
struct write_opts
|
struct write_opts
|
||||||
{
|
{
|
||||||
struct burn_drive *drive;
|
struct burn_drive *drive;
|
||||||
@ -36,6 +52,7 @@ struct write_opts
|
|||||||
struct burn_disc *disc;
|
struct burn_disc *disc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct w_list
|
struct w_list
|
||||||
{
|
{
|
||||||
struct burn_drive *drive;
|
struct burn_drive *drive;
|
||||||
@ -47,6 +64,7 @@ struct w_list
|
|||||||
{
|
{
|
||||||
struct scan_opts scan;
|
struct scan_opts scan;
|
||||||
struct erase_opts erase;
|
struct erase_opts erase;
|
||||||
|
struct format_opts format;
|
||||||
struct write_opts write;
|
struct write_opts write;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
@ -100,7 +118,13 @@ static void remove_worker(pthread_t th)
|
|||||||
free(a);
|
free(a);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(a != NULL); /* wasn't found.. this should not be possible */
|
|
||||||
|
/* ts A61006 */
|
||||||
|
/* a ssert(a != NULL);/ * wasn't found.. this should not be possible */
|
||||||
|
if (a == NULL)
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020101,
|
||||||
|
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"remove_worker() cannot find given worker item", 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *scan_worker_func(struct w_list *w)
|
static void *scan_worker_func(struct w_list *w)
|
||||||
@ -115,11 +139,41 @@ int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives)
|
|||||||
struct scan_opts o;
|
struct scan_opts o;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
/* ts A61006 : moved up from burn_drive_scan_sync , former Assert */
|
||||||
|
if (!burn_running) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020109,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Library not running (on attempt to scan)", 0, 0);
|
||||||
|
*drives = NULL;
|
||||||
|
*n_drives = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* cant be anything working! */
|
/* cant be anything working! */
|
||||||
assert(!(workers && workers->drive));
|
|
||||||
|
/* ts A61006 */
|
||||||
|
/* a ssert(!(workers && workers->drive)); */
|
||||||
|
if (workers != NULL && workers->drive != NULL) {
|
||||||
|
drive_is_active:;
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020102,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"A drive operation is still going on (want to scan)",
|
||||||
|
0, 0);
|
||||||
|
*drives = NULL;
|
||||||
|
*n_drives = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!workers) {
|
if (!workers) {
|
||||||
/* start it */
|
/* start it */
|
||||||
|
|
||||||
|
/* ts A61007 : test moved up from burn_drive_scan_sync()
|
||||||
|
was burn_wait_all() */
|
||||||
|
if (!burn_drives_are_clear())
|
||||||
|
goto drive_is_active;
|
||||||
|
*drives = NULL;
|
||||||
|
*n_drives = 0;
|
||||||
|
|
||||||
o.drives = drives;
|
o.drives = drives;
|
||||||
o.n_drives = n_drives;
|
o.n_drives = n_drives;
|
||||||
o.done = 0;
|
o.done = 0;
|
||||||
@ -128,7 +182,17 @@ int burn_drive_scan(struct burn_drive_info *drives[], unsigned int *n_drives)
|
|||||||
/* its done */
|
/* its done */
|
||||||
ret = workers->u.scan.done;
|
ret = workers->u.scan.done;
|
||||||
remove_worker(workers->thread);
|
remove_worker(workers->thread);
|
||||||
assert(workers == NULL);
|
|
||||||
|
/* ts A61006 */
|
||||||
|
/* a ssert(workers == NULL); */
|
||||||
|
if (workers != NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020101,
|
||||||
|
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"After scan a drive operation is still going on",
|
||||||
|
0, 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* still going */
|
/* still going */
|
||||||
}
|
}
|
||||||
@ -146,15 +210,106 @@ void burn_disc_erase(struct burn_drive *drive, int fast)
|
|||||||
{
|
{
|
||||||
struct erase_opts o;
|
struct erase_opts o;
|
||||||
|
|
||||||
assert(drive);
|
/* A70103 : will be set to 0 by burn_disc_erase_sync() */
|
||||||
assert(!SCAN_GOING());
|
drive->cancel = 1;
|
||||||
assert(!find_worker(drive));
|
|
||||||
|
/* ts A61006 */
|
||||||
|
/* a ssert(drive); */
|
||||||
|
/* a ssert(!SCAN_GOING()); */
|
||||||
|
/* a ssert(!find_worker(drive)); */
|
||||||
|
if((drive == NULL)) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||||
|
0x00020104,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"NULL pointer caught in burn_disc_erase", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((SCAN_GOING()) || find_worker(drive)) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||||
|
0x00020102,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"A drive operation is still going on (want to erase)",
|
||||||
|
0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A70103 moved up from burn_disc_erase_sync() */
|
||||||
|
/* ts A60825 : allow on parole to blank appendable CDs */
|
||||||
|
/* ts A70131 : allow blanking of overwriteable DVD-RW (profile 0x13) */
|
||||||
|
if ( ! (drive->status == BURN_DISC_FULL ||
|
||||||
|
(drive->status == BURN_DISC_APPENDABLE &&
|
||||||
|
! libburn_back_hack_42) ||
|
||||||
|
drive->current_profile == 0x13 ) ) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||||
|
0x00020130,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Drive and media state unsuitable for blanking",
|
||||||
|
0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
o.drive = drive;
|
o.drive = drive;
|
||||||
o.fast = fast;
|
o.fast = fast;
|
||||||
add_worker(drive, (WorkerFunc) erase_worker_func, &o);
|
add_worker(drive, (WorkerFunc) erase_worker_func, &o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61230 */
|
||||||
|
static void *format_worker_func(struct w_list *w)
|
||||||
|
{
|
||||||
|
burn_disc_format_sync(w->u.format.drive, w->u.format.size,
|
||||||
|
w->u.format.flag);
|
||||||
|
remove_worker(pthread_self());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61230 */
|
||||||
|
void burn_disc_format(struct burn_drive *drive, off_t size, int flag)
|
||||||
|
{
|
||||||
|
struct format_opts o;
|
||||||
|
int ok = 0;
|
||||||
|
char msg[160];
|
||||||
|
|
||||||
|
if ((SCAN_GOING()) || find_worker(drive)) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||||
|
0x00020102,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"A drive operation is still going on (want to format)",
|
||||||
|
0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flag & 128) /* application prescribed format type */
|
||||||
|
flag |= 16; /* enforce re-format */
|
||||||
|
|
||||||
|
if (drive->current_profile == 0x14)
|
||||||
|
ok = 1; /* DVD-RW sequential */
|
||||||
|
else if (drive->current_profile == 0x13 && (flag & 16))
|
||||||
|
ok = 1; /* DVD-RW Restricted Overwrite with force bit */
|
||||||
|
else if (drive->current_profile == 0x1a) {
|
||||||
|
ok = 1; /* DVD+RW */
|
||||||
|
size = 0;
|
||||||
|
flag &= ~(2|8); /* no insisting in size 0, no expansion */
|
||||||
|
flag |= 4; /* format up to maximum size */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
sprintf(msg,"Will not format media type %4.4Xh",
|
||||||
|
drive->current_profile);
|
||||||
|
libdax_msgs_submit(libdax_messenger, drive->global_index,
|
||||||
|
0x00020129,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, 0, 0);
|
||||||
|
drive->cancel = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
o.drive = drive;
|
||||||
|
o.size = size;
|
||||||
|
o.flag = flag;
|
||||||
|
add_worker(drive, (WorkerFunc) format_worker_func, &o);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void *write_disc_worker_func(struct w_list *w)
|
static void *write_disc_worker_func(struct w_list *w)
|
||||||
{
|
{
|
||||||
burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
|
burn_disc_write_sync(w->u.write.opts, w->u.write.disc);
|
||||||
@ -170,9 +325,57 @@ static void *write_disc_worker_func(struct w_list *w)
|
|||||||
void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
|
void burn_disc_write(struct burn_write_opts *opts, struct burn_disc *disc)
|
||||||
{
|
{
|
||||||
struct write_opts o;
|
struct write_opts o;
|
||||||
|
int i, j, mode, mixed_mode = 0;
|
||||||
|
|
||||||
|
/* For the next lines any return indicates failure */
|
||||||
|
opts->drive->cancel = 1;
|
||||||
|
|
||||||
|
/* ts A70203 : people have been warned in API specs */
|
||||||
|
if (opts->write_type == BURN_WRITE_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ts A61006 */
|
||||||
|
/* a ssert(!SCAN_GOING()); */
|
||||||
|
/* a ssert(!find_worker(opts->drive)); */
|
||||||
|
if ((SCAN_GOING()) || find_worker(opts->drive)) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, opts->drive->global_index,
|
||||||
|
0x00020102,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"A drive operation is still going on (want to write)",
|
||||||
|
0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* ts A61007 : obsolete Assert in spc_select_write_params() */
|
||||||
|
if (!opts->drive->mdata->valid) {
|
||||||
|
libdax_msgs_submit(libdax_messenger,
|
||||||
|
opts->drive->global_index, 0x00020113,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Drive capabilities not inquired yet", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* ts A61009 : obsolete Assert in sector_headers() */
|
||||||
|
if (! burn_disc_write_is_ok(opts, disc)) /* issues own msgs */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* ts A70122 : libburn SAO code mishandles mode changes */
|
||||||
|
for (i = 0; i < disc->sessions; i++) {
|
||||||
|
if (disc->session[i]->tracks <= 0)
|
||||||
|
continue;
|
||||||
|
mode = disc->session[i]->track[0]->mode;
|
||||||
|
for (j = 1; j < disc->session[i]->tracks; j++)
|
||||||
|
if (mode != disc->session[i]->track[j]->mode)
|
||||||
|
mixed_mode = 1;
|
||||||
|
}
|
||||||
|
if (mixed_mode && opts->write_type == BURN_WRITE_SAO) {
|
||||||
|
libdax_msgs_submit(libdax_messenger,
|
||||||
|
opts->drive->global_index, 0x00020133,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Cannot mix data and audio in SAO mode", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts->drive->cancel = 0; /* End of the return = failure area */
|
||||||
|
|
||||||
assert(!SCAN_GOING());
|
|
||||||
assert(!find_worker(opts->drive));
|
|
||||||
o.drive = opts->drive;
|
o.drive = opts->drive;
|
||||||
o.opts = opts;
|
o.opts = opts;
|
||||||
o.disc = disc;
|
o.disc = disc;
|
||||||
|
215
libburn/cleanup.c
Normal file
215
libburn/cleanup.c
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
A signal handler which cleans up an application and exits.
|
||||||
|
|
||||||
|
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
cc -g -o cleanup -DCleanup_standalonE cleanup.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
typedef void (*sighandler_t)(int);
|
||||||
|
|
||||||
|
|
||||||
|
#include "cleanup.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef Cleanup_has_no_libburn_os_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "../libburn/os.h"
|
||||||
|
|
||||||
|
/* see os.h for name of particular os-*.h where this is defined */
|
||||||
|
static int signal_list[]= { BURN_OS_SIGNAL_MACRO_LIST , -1};
|
||||||
|
static char *signal_name_list[]= { BURN_OS_SIGNAL_NAME_LIST , "@"};
|
||||||
|
static int signal_list_count= BURN_OS_SIGNAL_COUNT;
|
||||||
|
static int non_signal_list[]= { BURN_OS_NON_SIGNAL_MACRO_LIST, -1};
|
||||||
|
static int non_signal_list_count= BURN_OS_NON_SIGNAL_COUNT;
|
||||||
|
|
||||||
|
|
||||||
|
#else /* ! Cleanup_has_no_libburn_os_H */
|
||||||
|
|
||||||
|
|
||||||
|
/* Outdated. Linux only. For backward compatibility with pre-libburn-0.2.3 */
|
||||||
|
|
||||||
|
/* Signals to be caught */
|
||||||
|
static int signal_list[]= {
|
||||||
|
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
|
||||||
|
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM,
|
||||||
|
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN,
|
||||||
|
SIGTTOU,
|
||||||
|
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
|
||||||
|
SIGVTALRM, SIGXCPU, SIGXFSZ, -1
|
||||||
|
};
|
||||||
|
static char *signal_name_list[]= {
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT",
|
||||||
|
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM",
|
||||||
|
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN",
|
||||||
|
"SIGTTOU",
|
||||||
|
"SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP",
|
||||||
|
"SIGVTALRM", "SIGXCPU", "SIGXFSZ", "@"
|
||||||
|
};
|
||||||
|
static int signal_list_count= 24;
|
||||||
|
|
||||||
|
/* Signals not to be caught */
|
||||||
|
static int non_signal_list[]= {
|
||||||
|
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH, -1
|
||||||
|
};
|
||||||
|
static int non_signal_list_count= 5;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* Cleanup_has_no_libburn_os_H */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* run time dynamic part */
|
||||||
|
static char cleanup_msg[4096]= {""};
|
||||||
|
static int cleanup_exiting= 0;
|
||||||
|
static int cleanup_has_reported= -1234567890;
|
||||||
|
|
||||||
|
static void *cleanup_app_handle= NULL;
|
||||||
|
static Cleanup_app_handler_T cleanup_app_handler= NULL;
|
||||||
|
static int cleanup_perform_app_handler_first= 0;
|
||||||
|
|
||||||
|
|
||||||
|
static int Cleanup_handler_exit(int exit_value, int signum, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if(cleanup_msg[0]!=0 && cleanup_has_reported!=signum) {
|
||||||
|
fprintf(stderr,"\n%s\n",cleanup_msg);
|
||||||
|
cleanup_has_reported= signum;
|
||||||
|
}
|
||||||
|
if(cleanup_perform_app_handler_first)
|
||||||
|
if(cleanup_app_handler!=NULL) {
|
||||||
|
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
|
if(ret==2 || ret==-2)
|
||||||
|
return(2);
|
||||||
|
}
|
||||||
|
if(cleanup_exiting) {
|
||||||
|
fprintf(stderr,"cleanup: ABORT : repeat by pid=%d, signum=%d\n",
|
||||||
|
getpid(),signum);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
cleanup_exiting= 1;
|
||||||
|
alarm(0);
|
||||||
|
if(!cleanup_perform_app_handler_first)
|
||||||
|
if(cleanup_app_handler!=NULL) {
|
||||||
|
ret= (*cleanup_app_handler)(cleanup_app_handle,signum,0);
|
||||||
|
if(ret==2 || ret==-2)
|
||||||
|
return(2);
|
||||||
|
}
|
||||||
|
exit(exit_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Cleanup_handler_generic(int signum)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sprintf(cleanup_msg,"UNIX-SIGNAL caught: %d errno= %d",signum,errno);
|
||||||
|
for(i= 0; i<signal_list_count; i++)
|
||||||
|
if(signum==signal_list[i]) {
|
||||||
|
sprintf(cleanup_msg,"UNIX-SIGNAL: %s errno= %d",
|
||||||
|
signal_name_list[i],errno);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Cleanup_handler_exit(1,signum,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler, int flag)
|
||||||
|
/*
|
||||||
|
bit0= set to default handlers
|
||||||
|
bit1= set to ignore
|
||||||
|
bit2= set cleanup_perform_app_handler_first
|
||||||
|
bit3= set SIGABRT to handler (makes sense with bits 0 or 1)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
int i,j,max_sig= -1,min_sig= 0x7fffffff;
|
||||||
|
sighandler_t sig_handler;
|
||||||
|
|
||||||
|
cleanup_msg[0]= 0;
|
||||||
|
cleanup_app_handle= handle;
|
||||||
|
cleanup_app_handler= handler;
|
||||||
|
|
||||||
|
/* <<< make cleanup_exiting thread safe to get rid of this */
|
||||||
|
if(flag&4)
|
||||||
|
cleanup_perform_app_handler_first= 1;
|
||||||
|
|
||||||
|
|
||||||
|
if(flag&1)
|
||||||
|
sig_handler= SIG_DFL;
|
||||||
|
else if(flag&2)
|
||||||
|
sig_handler= SIG_IGN;
|
||||||
|
else
|
||||||
|
sig_handler= Cleanup_handler_generic;
|
||||||
|
/* set all signal numbers between the lowest and highest in the list
|
||||||
|
except those in the non-signal list */
|
||||||
|
for(i= 0; i<signal_list_count; i++) {
|
||||||
|
if(signal_list[i]>max_sig)
|
||||||
|
max_sig= signal_list[i];
|
||||||
|
if(signal_list[i]<min_sig)
|
||||||
|
min_sig= signal_list[i];
|
||||||
|
}
|
||||||
|
for(i= min_sig; i<=max_sig; i++) {
|
||||||
|
for(j= 0; j<non_signal_list_count; j++)
|
||||||
|
if(i==non_signal_list[j])
|
||||||
|
break;
|
||||||
|
if(j>=non_signal_list_count) {
|
||||||
|
if(i==SIGABRT && (flag&8))
|
||||||
|
signal(i,Cleanup_handler_generic);
|
||||||
|
else
|
||||||
|
signal(i,sig_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef Cleanup_standalonE
|
||||||
|
|
||||||
|
struct Demo_apP {
|
||||||
|
char *msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int Demo_app_handler(struct Demo_apP *demoapp, int signum, int flag)
|
||||||
|
{
|
||||||
|
printf("Handling exit of demo application on signal %d. msg=\"%s\"\n",
|
||||||
|
signum,demoapp->msg);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
struct Demo_apP demoapp;
|
||||||
|
|
||||||
|
demoapp.msg= "Good Bye";
|
||||||
|
Cleanup_set_handlers(&demoapp,(Cleanup_app_handler_T) Demo_app_handler,0);
|
||||||
|
|
||||||
|
if(1) { /* change to 0 in order to wait for external signals */
|
||||||
|
char *cpt= NULL,c;
|
||||||
|
printf("Intentionally provoking SIGSEGV ...\n");
|
||||||
|
c= *cpt;
|
||||||
|
} else {
|
||||||
|
printf("killme: %d\n",getpid());
|
||||||
|
sleep(3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cleanup_set_handlers(NULL,NULL,1);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Cleanup_standalonE */
|
34
libburn/cleanup.h
Normal file
34
libburn/cleanup.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
cleanup.c , Copyright 2006 Thomas Schmitt <scdbackup@gmx.net>
|
||||||
|
|
||||||
|
A signal handler which cleans up an application and exits.
|
||||||
|
|
||||||
|
Provided under GPL license within GPL projects, BSD license elsewise.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Cleanup_includeD
|
||||||
|
#define Cleanup_includeD 1
|
||||||
|
|
||||||
|
|
||||||
|
/** Layout of an application provided cleanup function using an application
|
||||||
|
provided handle as first argument and the signal number as second
|
||||||
|
argument. The third argument is a flag bit field with no defined bits yet.
|
||||||
|
If the handler returns 2 or -2 then it has delegated exit() to some other
|
||||||
|
instance and the Cleanup handler shall return rather than exit.
|
||||||
|
*/
|
||||||
|
typedef int (*Cleanup_app_handler_T)(void *, int, int);
|
||||||
|
|
||||||
|
|
||||||
|
/** Establish exiting signal handlers on (hopefully) all signals that are
|
||||||
|
not ignored by default or non-catchable.
|
||||||
|
@param handle Opaque object which knows how to cleanup application
|
||||||
|
@param handler Function which uses handle to perform application cleanup
|
||||||
|
@param flag Control Bitfield
|
||||||
|
bit0= reset to default signal handling
|
||||||
|
*/
|
||||||
|
int Cleanup_set_handlers(void *handle, Cleanup_app_handler_T handler,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ! Cleanup_includeD */
|
||||||
|
|
1273
libburn/drive.c
1273
libburn/drive.c
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,8 @@
|
|||||||
struct burn_drive;
|
struct burn_drive;
|
||||||
struct command;
|
struct command;
|
||||||
struct mempage;
|
struct mempage;
|
||||||
|
struct scsi_mode_data;
|
||||||
|
struct burn_speed_descriptor;
|
||||||
|
|
||||||
#define LEAD_IN 1
|
#define LEAD_IN 1
|
||||||
#define GAP 2
|
#define GAP 2
|
||||||
@ -35,9 +37,14 @@ struct mempage;
|
|||||||
SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))
|
SESSION_LEADOUT_ENTRY((d), (d)->toc->sessions-1))
|
||||||
|
|
||||||
struct burn_drive *burn_drive_register(struct burn_drive *);
|
struct burn_drive *burn_drive_register(struct burn_drive *);
|
||||||
|
int burn_drive_unregister(struct burn_drive *d);
|
||||||
|
|
||||||
unsigned int burn_drive_count(void);
|
unsigned int burn_drive_count(void);
|
||||||
void burn_wait_all(void);
|
|
||||||
|
/* ts A61007 */
|
||||||
|
/* void burn_wait_all(void); */
|
||||||
|
int burn_drives_are_clear(void);
|
||||||
|
|
||||||
int burn_sector_length_write(struct burn_drive *d);
|
int burn_sector_length_write(struct burn_drive *d);
|
||||||
int burn_track_control(struct burn_drive *d, int);
|
int burn_track_control(struct burn_drive *d, int);
|
||||||
void burn_write_empty_sector(int fd);
|
void burn_write_empty_sector(int fd);
|
||||||
@ -51,7 +58,54 @@ void burn_disc_erase_sync(struct burn_drive *d, int fast);
|
|||||||
int burn_drive_get_block_types(struct burn_drive *d,
|
int burn_drive_get_block_types(struct burn_drive *d,
|
||||||
enum burn_write_types write_type);
|
enum burn_write_types write_type);
|
||||||
|
|
||||||
/* ts A60822 */
|
|
||||||
int burn_drive_is_open(struct burn_drive *d);
|
int burn_drive_is_open(struct burn_drive *d);
|
||||||
|
int burn_drive_is_occupied(struct burn_drive *d);
|
||||||
|
int burn_drive_forget(struct burn_drive *d, int force);
|
||||||
|
int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count);
|
||||||
|
|
||||||
|
/* ts A61021 : the unspecific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int burn_setup_drive(struct burn_drive *d, char *fname);
|
||||||
|
|
||||||
|
/* ts A61021 : after-setup activities from sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
struct burn_drive *burn_drive_finish_enum(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61125 : media status aspects of burn_drive_grab() */
|
||||||
|
int burn_drive_inquire_media(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61125 : model aspects of burn_drive_release */
|
||||||
|
int burn_drive_mark_unready(struct burn_drive *d);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61226 */
|
||||||
|
int burn_speed_descriptor_new(struct burn_speed_descriptor **s,
|
||||||
|
struct burn_speed_descriptor *prev,
|
||||||
|
struct burn_speed_descriptor *next, int flag);
|
||||||
|
|
||||||
|
/* ts A61226 */
|
||||||
|
/* @param flag bit0= destroy whole next-chain of descriptors */
|
||||||
|
int burn_speed_descriptor_destroy(struct burn_speed_descriptor **s, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61226 : free dynamically allocated sub data of struct scsi_mode_data */
|
||||||
|
int burn_mdata_free_subs(struct scsi_mode_data *m);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61230 */
|
||||||
|
void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70207 : evaluate write mode related peculiarities of a disc */
|
||||||
|
struct burn_disc_mode_demands {
|
||||||
|
int multi_session;
|
||||||
|
int multi_track;
|
||||||
|
int unknown_track_size;
|
||||||
|
int mixed_mode;
|
||||||
|
int audio;
|
||||||
|
int exotic_track;
|
||||||
|
};
|
||||||
|
int burn_disc_get_write_mode_demands(struct burn_disc *disc,
|
||||||
|
struct burn_disc_mode_demands *result, int flag);
|
||||||
|
|
||||||
#endif /* __DRIVE */
|
#endif /* __DRIVE */
|
||||||
|
@ -42,7 +42,7 @@ static int file_read(struct burn_source *source,
|
|||||||
unsigned char *buffer,
|
unsigned char *buffer,
|
||||||
int size)
|
int size)
|
||||||
{
|
{
|
||||||
struct burn_source_fd *fs = source->data;
|
struct burn_source_file *fs = source->data;
|
||||||
|
|
||||||
return read_full_buffer(fs->datafd, buffer, size);
|
return read_full_buffer(fs->datafd, buffer, size);
|
||||||
}
|
}
|
||||||
@ -71,14 +71,24 @@ static off_t file_size(struct burn_source *source)
|
|||||||
struct stat buf;
|
struct stat buf;
|
||||||
struct burn_source_file *fs = source->data;
|
struct burn_source_file *fs = source->data;
|
||||||
|
|
||||||
|
if (fs->fixed_size > 0)
|
||||||
|
return fs->fixed_size;
|
||||||
if (fstat(fs->datafd, &buf) == -1)
|
if (fstat(fs->datafd, &buf) == -1)
|
||||||
return (off_t) 0;
|
return (off_t) 0;
|
||||||
/* for now we keep it compatible to the old (int) return value */
|
|
||||||
if(buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
|
|
||||||
return (off_t) 1308622848;
|
|
||||||
return (off_t) buf.st_size;
|
return (off_t) buf.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70125 */
|
||||||
|
static int file_set_size(struct burn_source *source, off_t size)
|
||||||
|
{
|
||||||
|
struct burn_source_file *fs = source->data;
|
||||||
|
|
||||||
|
fs->fixed_size = size;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct burn_source *burn_file_source_new(const char *path, const char *subpath)
|
struct burn_source *burn_file_source_new(const char *path, const char *subpath)
|
||||||
{
|
{
|
||||||
struct burn_source_file *fs;
|
struct burn_source_file *fs;
|
||||||
@ -103,84 +113,43 @@ struct burn_source *burn_file_source_new(const char *path, const char *subpath)
|
|||||||
if (subpath)
|
if (subpath)
|
||||||
fs->subfd = fd2;
|
fs->subfd = fd2;
|
||||||
|
|
||||||
|
/* ts A70125 */
|
||||||
|
fs->fixed_size = 0;
|
||||||
|
|
||||||
src = burn_source_new();
|
src = burn_source_new();
|
||||||
src->read = file_read;
|
src->read = file_read;
|
||||||
if (subpath)
|
if (subpath)
|
||||||
src->read_sub = file_read_sub;
|
src->read_sub = file_read_sub;
|
||||||
|
|
||||||
src->get_size = file_size;
|
src->get_size = file_size;
|
||||||
|
src->set_size = file_set_size;
|
||||||
src->free_data = file_free;
|
src->free_data = file_free;
|
||||||
src->data = fs;
|
src->data = fs;
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------ provisory location for the new source subclass fd --------- */
|
/* ts A70126 : removed class burn_source_fd in favor of burn_source_file */
|
||||||
|
|
||||||
static off_t fd_get_size(struct burn_source *source)
|
|
||||||
{
|
|
||||||
struct stat buf;
|
|
||||||
struct burn_source_fd *fs = source->data;
|
|
||||||
|
|
||||||
if (fs->fixed_size > 0)
|
|
||||||
return fs->fixed_size;
|
|
||||||
if (fstat(fs->datafd, &buf) == -1)
|
|
||||||
return (off_t) 0;
|
|
||||||
/* for now we keep it compatible to the old (int) return value */
|
|
||||||
if (buf.st_size >= 1308622848) /* 2 GB - 800 MB to prevent rollover */
|
|
||||||
return (off_t) 1308622848;
|
|
||||||
return buf.st_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fd_read(struct burn_source *source,
|
|
||||||
unsigned char *buffer,
|
|
||||||
int size)
|
|
||||||
{
|
|
||||||
struct burn_source_fd *fs = source->data;
|
|
||||||
|
|
||||||
return read_full_buffer(fs->datafd, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int fd_read_sub(struct burn_source *source,
|
|
||||||
unsigned char *buffer,
|
|
||||||
int size)
|
|
||||||
{
|
|
||||||
struct burn_source_fd *fs = source->data;
|
|
||||||
|
|
||||||
return read_full_buffer(fs->subfd, buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void fd_free_data(struct burn_source *source)
|
|
||||||
{
|
|
||||||
struct burn_source_fd *fs = source->data;
|
|
||||||
|
|
||||||
close(fs->datafd);
|
|
||||||
if (source->read_sub)
|
|
||||||
close(fs->subfd);
|
|
||||||
free(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size)
|
struct burn_source *burn_fd_source_new(int datafd, int subfd, off_t size)
|
||||||
{
|
{
|
||||||
struct burn_source_fd *fs;
|
struct burn_source_file *fs;
|
||||||
struct burn_source *src;
|
struct burn_source *src;
|
||||||
|
|
||||||
if (datafd == -1)
|
if (datafd == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
fs = malloc(sizeof(struct burn_source_fd));
|
fs = malloc(sizeof(struct burn_source_file));
|
||||||
fs->datafd = datafd;
|
fs->datafd = datafd;
|
||||||
fs->subfd = subfd;
|
fs->subfd = subfd;
|
||||||
fs->fixed_size = size;
|
fs->fixed_size = size;
|
||||||
|
|
||||||
src = burn_source_new();
|
src = burn_source_new();
|
||||||
src->read = fd_read;
|
src->read = file_read;
|
||||||
if(subfd != -1)
|
if(subfd != -1)
|
||||||
src->read = fd_read_sub;
|
src->read = file_read_sub;
|
||||||
src->get_size = fd_get_size;
|
src->get_size = file_size;
|
||||||
src->free_data = fd_free_data;
|
src->set_size = file_set_size;
|
||||||
|
src->free_data = file_free;
|
||||||
src->data = fs;
|
src->data = fs;
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,14 @@
|
|||||||
#define BURN__FILE_H
|
#define BURN__FILE_H
|
||||||
|
|
||||||
struct burn_source_file
|
struct burn_source_file
|
||||||
{
|
|
||||||
int datafd;
|
|
||||||
int subfd;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ------ provisory location for the new source subclass fd --------- */
|
|
||||||
|
|
||||||
struct burn_source_fd
|
|
||||||
{
|
{
|
||||||
int datafd;
|
int datafd;
|
||||||
int subfd;
|
int subfd;
|
||||||
off_t fixed_size;
|
off_t fixed_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70126 : burn_source_file obsoleted burn_source_fd */
|
||||||
|
|
||||||
|
|
||||||
#endif /* LIBBURN__FILE_H */
|
#endif /* LIBBURN__FILE_H */
|
||||||
|
238
libburn/init.c
238
libburn/init.c
@ -1,9 +1,14 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
|
||||||
|
/* ts A61007 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "sg.h"
|
#include "sg.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@ -14,6 +19,11 @@
|
|||||||
#define BURN_BACK_HACKS_INIT 1
|
#define BURN_BACK_HACKS_INIT 1
|
||||||
#include "back_hacks.h"
|
#include "back_hacks.h"
|
||||||
|
|
||||||
|
/* ts A60924 : a new message handling facility */
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
struct libdax_msgs *libdax_messenger= NULL;
|
||||||
|
|
||||||
|
|
||||||
int burn_running = 0;
|
int burn_running = 0;
|
||||||
|
|
||||||
/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */
|
/* ts A60813 : wether to use O_EXCL and/or O_NONBLOCK in libburn/sg.c */
|
||||||
@ -31,50 +41,236 @@ int burn_sg_open_o_nonblock = 1;
|
|||||||
int burn_sg_open_abort_busy = 0;
|
int burn_sg_open_abort_busy = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61002 */
|
||||||
|
|
||||||
|
#include "cleanup.h"
|
||||||
|
|
||||||
|
/* Parameters for builtin abort handler */
|
||||||
|
static char abort_message_prefix[81] = {"libburn : "};
|
||||||
|
static pid_t abort_control_pid= 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A60925 : ticket 74 */
|
||||||
|
/** Create the messenger object for libburn. */
|
||||||
|
int burn_msgs_initialize(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if(libdax_messenger == NULL) {
|
||||||
|
ret = libdax_msgs_new(&libdax_messenger,0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
libdax_msgs_set_severities(libdax_messenger, LIBDAX_MSGS_SEV_NEVER,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, "libburn: ", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A60924 : ticket 74 : Added use of global libdax_messenger */
|
||||||
int burn_initialize(void)
|
int burn_initialize(void)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (burn_running)
|
if (burn_running)
|
||||||
return 1;
|
return 1;
|
||||||
|
ret = burn_msgs_initialize();
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
burn_running = 1;
|
burn_running = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_finish(void)
|
void burn_finish(void)
|
||||||
{
|
{
|
||||||
assert(burn_running);
|
/* ts A61007 : assume no messageing system */
|
||||||
|
/* a ssert(burn_running); */
|
||||||
|
if (!burn_running)
|
||||||
|
return;
|
||||||
|
|
||||||
burn_wait_all();
|
/* ts A61007 */
|
||||||
|
/* burn_wait_all(); */
|
||||||
|
if (!burn_drives_are_clear()) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020107,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Drive is busy on attempt to shut down library", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
|
/* ts A60904 : ticket 62, contribution by elmom : name addon "_all" */
|
||||||
burn_drive_free_all();
|
burn_drive_free_all();
|
||||||
|
|
||||||
|
/* ts A60924 : ticket 74 */
|
||||||
|
libdax_msgs_destroy(&libdax_messenger,0);
|
||||||
|
|
||||||
burn_running = 0;
|
burn_running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ts A60813 */
|
/* ts A60813 */
|
||||||
/** Set parameters for behavior on opening device files. To be called early
|
/** API function. See libburn.h */
|
||||||
after burn_initialize() and before any bus scan. But not mandatory at all.
|
|
||||||
@param exclusive Try to open only devices which are not marked as busy
|
|
||||||
and try to mark them busy if opened sucessfully. (O_EXCL)
|
|
||||||
There are kernels which simply don't care about O_EXCL.
|
|
||||||
Some have it off, some have it on, some are switchable.
|
|
||||||
@param blocking Try to wait for drives which do not open immediately but
|
|
||||||
also do not return an error as well. (O_NONBLOCK)
|
|
||||||
This might stall indefinitely with /dev/hdX hard disks.
|
|
||||||
@param abort_on_busy Unconditionally abort process when a non blocking
|
|
||||||
exclusive opening attempt indicates a busy drive.
|
|
||||||
Use this only after thorough tests with your app.
|
|
||||||
Parameter value 1 enables a feature, 0 disables.
|
|
||||||
Default is (1,0,0). Have a good reason before you change it.
|
|
||||||
*/
|
|
||||||
void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy)
|
void burn_preset_device_open(int exclusive, int blocking, int abort_on_busy)
|
||||||
{
|
{
|
||||||
assert(burn_running);
|
/* ts A61007 */
|
||||||
|
/* a ssert(burn_running); */
|
||||||
|
if (!burn_running)
|
||||||
|
return;
|
||||||
|
|
||||||
burn_sg_open_o_excl= !!exclusive;
|
burn_sg_open_o_excl= exclusive;
|
||||||
burn_sg_open_o_nonblock= !blocking;
|
burn_sg_open_o_nonblock= !blocking;
|
||||||
burn_sg_open_abort_busy= !!abort_on_busy;
|
burn_sg_open_abort_busy= !!abort_on_busy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A60924 : ticket 74 */
|
||||||
|
/** Control queueing and stderr printing of messages from libburn.
|
||||||
|
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||||
|
"NOTE", "UPDATE", "DEBUG", "ALL".
|
||||||
|
@param queue_severity Gives the minimum limit for messages to be queued.
|
||||||
|
Default: "NEVER". If you queue messages then you
|
||||||
|
must consume them by burn_msgs_obtain().
|
||||||
|
@param print_severity Does the same for messages to be printed directly
|
||||||
|
to stderr.
|
||||||
|
@param print_id A text prefix to be printed before the message.
|
||||||
|
@return >0 for success, <=0 for error
|
||||||
|
|
||||||
|
*/
|
||||||
|
int burn_msgs_set_severities(char *queue_severity,
|
||||||
|
char *print_severity, char *print_id)
|
||||||
|
{
|
||||||
|
int ret, queue_sevno, print_sevno;
|
||||||
|
|
||||||
|
ret = libdax_msgs__text_to_sev(queue_severity, &queue_sevno, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
ret = libdax_msgs__text_to_sev(print_severity, &print_sevno, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
ret = libdax_msgs_set_severities(libdax_messenger, queue_sevno,
|
||||||
|
print_sevno, print_id, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A60924 : ticket 74 */
|
||||||
|
#define BURM_MSGS_MESSAGE_LEN 4096
|
||||||
|
|
||||||
|
/** Obtain the oldest pending libburn message from the queue which has at
|
||||||
|
least the given minimum_severity. This message and any older message of
|
||||||
|
lower severity will get discarded from the queue and is then lost forever.
|
||||||
|
Severity may be one of "NEVER", "FATAL", "SORRY", "WARNING", "HINT",
|
||||||
|
"NOTE", "UPDATE", "DEBUG", "ALL". To call with minimum_severity "NEVER"
|
||||||
|
will discard the whole queue.
|
||||||
|
@param error_code Will become a unique error code as liste in
|
||||||
|
libburn/libdax_msgs.h
|
||||||
|
@param msg_text Must provide at least BURM_MSGS_MESSAGE_LEN bytes.
|
||||||
|
@param os_errno Will become the eventual errno related to the message
|
||||||
|
@param severity Will become the severity related to the message and
|
||||||
|
should provide at least 80 bytes.
|
||||||
|
@return 1 if a matching item was found, 0 if not, <0 for severe errors
|
||||||
|
*/
|
||||||
|
int burn_msgs_obtain(char *minimum_severity,
|
||||||
|
int *error_code, char msg_text[], int *os_errno,
|
||||||
|
char severity[])
|
||||||
|
{
|
||||||
|
int ret, minimum_sevno, sevno, priority;
|
||||||
|
char *textpt, *sev_name;
|
||||||
|
struct libdax_msgs_item *item = NULL;
|
||||||
|
|
||||||
|
ret = libdax_msgs__text_to_sev(minimum_severity, &minimum_sevno, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
ret = libdax_msgs_obtain(libdax_messenger, &item, minimum_sevno,
|
||||||
|
LIBDAX_MSGS_PRIO_ZERO, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
goto ex;
|
||||||
|
ret = libdax_msgs_item_get_msg(item, error_code, &textpt, os_errno, 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
goto ex;
|
||||||
|
strncpy(msg_text, textpt, BURM_MSGS_MESSAGE_LEN-1);
|
||||||
|
if(strlen(textpt) >= BURM_MSGS_MESSAGE_LEN)
|
||||||
|
msg_text[BURM_MSGS_MESSAGE_LEN-1] = 0;
|
||||||
|
|
||||||
|
severity[0]= 0;
|
||||||
|
ret = libdax_msgs_item_get_rank(item, &sevno, &priority, 0);
|
||||||
|
if(ret <= 0)
|
||||||
|
goto ex;
|
||||||
|
ret = libdax_msgs__sev_to_text(sevno, &sev_name, 0);
|
||||||
|
if(ret <= 0)
|
||||||
|
goto ex;
|
||||||
|
strcpy(severity,sev_name);
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
ex:
|
||||||
|
libdax_msgs_destroy_item(libdax_messenger, &item, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int burn_builtin_abort_handler(void *handle, int signum, int flag)
|
||||||
|
{
|
||||||
|
if(getpid() != abort_control_pid) {
|
||||||
|
|
||||||
|
#ifdef Not_yeT
|
||||||
|
pthread_t thread_id;
|
||||||
|
|
||||||
|
/* >>> need better handling of self-induced SIGs
|
||||||
|
like SIGSEGV or SIGFPE.
|
||||||
|
Like bonking the control thread if it did not show up
|
||||||
|
after a short while.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* >>> if this is a non-fatal signal : return -2 */
|
||||||
|
|
||||||
|
thread_id = pthread_self();
|
||||||
|
/* >>> find thread_id in worker list of async.c */
|
||||||
|
/* >>> if owning a drive : mark idle and canceled
|
||||||
|
(can't do anything more) */
|
||||||
|
|
||||||
|
usleep(1000000); /* calm down */
|
||||||
|
|
||||||
|
/* forward signal to control thread */
|
||||||
|
if (abort_control_pid>1)
|
||||||
|
kill(abort_control_pid, signum);
|
||||||
|
|
||||||
|
/* >>> ??? end thread */;
|
||||||
|
|
||||||
|
#else
|
||||||
|
usleep(1000000); /* calm down */
|
||||||
|
return -2;
|
||||||
|
#endif /* ! Not_yeT */
|
||||||
|
|
||||||
|
}
|
||||||
|
Cleanup_set_handlers(NULL, NULL, 2);
|
||||||
|
fprintf(stderr,"%sABORT : Trying to shut down drive and library\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
fprintf(stderr,
|
||||||
|
"%sABORT : Wait the normal burning time before any kill -9\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
close(0); /* somehow stdin as input blocks abort until EOF */
|
||||||
|
burn_abort(4440, burn_abort_pacifier, abort_message_prefix);
|
||||||
|
fprintf(stderr,
|
||||||
|
"\n%sABORT : Program done. Even if you do not see a shell prompt.\n\n",
|
||||||
|
abort_message_prefix);
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void burn_set_signal_handling(void *handle, burn_abort_handler_t handler,
|
||||||
|
int mode)
|
||||||
|
{
|
||||||
|
if(handler == NULL && mode == 0) {
|
||||||
|
handler = burn_builtin_abort_handler;
|
||||||
|
/*
|
||||||
|
fprintf(stderr, "libburn_experimental: activated burn_builtin_abort_handler() with handle '%s'\n",(handle==NULL ? "libburn : " : (char *) handle));
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
strcpy(abort_message_prefix, "libburn : ");
|
||||||
|
if(handle != NULL)
|
||||||
|
strncpy(abort_message_prefix, (char *) handle,
|
||||||
|
sizeof(abort_message_prefix)-1);
|
||||||
|
abort_message_prefix[sizeof(abort_message_prefix)-1] = 0;
|
||||||
|
abort_control_pid= getpid();
|
||||||
|
Cleanup_set_handlers(handle, (Cleanup_app_handler_T) handler, mode|4);
|
||||||
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
326
libburn/libdax_audioxtr.c
Normal file
326
libburn/libdax_audioxtr.c
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
|
||||||
|
/* libdax_audioxtr
|
||||||
|
Audio track data extraction facility of libdax and libburn.
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
|
||||||
|
/* Only this single source module is entitled to do this */
|
||||||
|
#define LIBDAX_AUDIOXTR_H_INTERNAL 1
|
||||||
|
|
||||||
|
/* All clients of the extraction facility must do this */
|
||||||
|
#include "libdax_audioxtr.h"
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag)
|
||||||
|
{
|
||||||
|
int ret= -1;
|
||||||
|
struct libdax_audioxtr *o;
|
||||||
|
|
||||||
|
o= *xtr= (struct libdax_audioxtr *) malloc(sizeof(struct libdax_audioxtr));
|
||||||
|
if(o==NULL)
|
||||||
|
return(-1);
|
||||||
|
strncpy(o->path,path,LIBDAX_AUDIOXTR_STRLEN-1);
|
||||||
|
o->path[LIBDAX_AUDIOXTR_STRLEN]= 0;
|
||||||
|
o->fd= -1;
|
||||||
|
strcpy(o->fmt,"unidentified");
|
||||||
|
o->fmt_info[0]= 0;
|
||||||
|
o->data_size= 0;
|
||||||
|
o->extract_count= 0;
|
||||||
|
|
||||||
|
o->num_channels= 0;
|
||||||
|
o->sample_rate= 0;
|
||||||
|
o->bits_per_sample= 0;
|
||||||
|
o->msb_first= 0;
|
||||||
|
|
||||||
|
o->wav_subchunk2_size= 0;
|
||||||
|
|
||||||
|
o->au_data_location= 0;
|
||||||
|
o->au_data_size= 0xffffffff;
|
||||||
|
|
||||||
|
ret= libdax_audioxtr_open(o,0);
|
||||||
|
if(ret<=0)
|
||||||
|
{ret= -2*(ret<0); goto failure;}
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
failure:
|
||||||
|
libdax_audioxtr_destroy(xtr,0);
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag)
|
||||||
|
{
|
||||||
|
struct libdax_audioxtr *o;
|
||||||
|
|
||||||
|
o= *xtr;
|
||||||
|
if(o==NULL)
|
||||||
|
return(0);
|
||||||
|
if(o->fd>=0 && strcmp(o->path,"-")!=0)
|
||||||
|
close(o->fd);
|
||||||
|
free((char *) o);
|
||||||
|
*xtr= NULL;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char msg[LIBDAX_AUDIOXTR_STRLEN+80];
|
||||||
|
|
||||||
|
if(strcmp(o->path,"-")==0)
|
||||||
|
o->fd= 0;
|
||||||
|
else
|
||||||
|
o->fd= open(o->path, O_RDONLY);
|
||||||
|
if(o->fd<0) {
|
||||||
|
sprintf(msg,"Cannot open audio source file : %s",o->path);
|
||||||
|
libdax_msgs_submit(libdax_messenger,-1,0x00020200,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, errno, 0);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
ret= libdax_audioxtr_identify(o,0);
|
||||||
|
if(ret<=0) {
|
||||||
|
sprintf(msg,"Audio source file has unsuitable format : %s",o->path);
|
||||||
|
libdax_msgs_submit(libdax_messenger,-1,0x00020201,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, 0, 0);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
ret= libdax_audioxtr_init_reading(o,0);
|
||||||
|
if(ret<=0) {
|
||||||
|
sprintf(msg,"Failed to prepare reading of audio data : %s",o->path);
|
||||||
|
libdax_msgs_submit(libdax_messenger,-1,0x00020202,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, 0, 0);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char buf[45];
|
||||||
|
|
||||||
|
/* check wether this is a MS WAVE file .wav */
|
||||||
|
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||||
|
|
||||||
|
if(o->fd!=0) {
|
||||||
|
ret= lseek(o->fd,0,SEEK_SET);
|
||||||
|
if(ret==-1)
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
ret= read(o->fd, buf, 44);
|
||||||
|
if(ret<44)
|
||||||
|
return(0);
|
||||||
|
buf[44]= 0; /* as stopper for any string operations */
|
||||||
|
|
||||||
|
if(strncmp(buf,"RIFF",4)!=0) /* ChunkID */
|
||||||
|
return(0);
|
||||||
|
if(strncmp(buf+8,"WAVE",4)!=0) /* Format */
|
||||||
|
return(0);
|
||||||
|
if(strncmp(buf+12,"fmt ",4)!=0) /* Subchunk1ID */
|
||||||
|
return(0);
|
||||||
|
if(buf[16]!=16 || buf[17]!=0 || buf[18]!=0 || buf[19]!=0) /* Subchunk1Size */
|
||||||
|
return(0);
|
||||||
|
if(buf[20]!=1 || buf[21]!=0) /* AudioFormat must be 1 (Linear quantization) */
|
||||||
|
return(0);
|
||||||
|
|
||||||
|
strcpy(o->fmt,".wav");
|
||||||
|
o->msb_first= 0;
|
||||||
|
o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *) buf+22,2,0);
|
||||||
|
o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *) buf+24,4,0);
|
||||||
|
o->bits_per_sample= libdax_audioxtr_to_int(o,(unsigned char *)buf+34,2,0);
|
||||||
|
sprintf(o->fmt_info,
|
||||||
|
".wav , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
||||||
|
o->num_channels,o->sample_rate,o->bits_per_sample);
|
||||||
|
o->wav_subchunk2_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+40,4,0);
|
||||||
|
o->data_size= o->wav_subchunk2_size;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag)
|
||||||
|
{
|
||||||
|
int ret,encoding;
|
||||||
|
char buf[24];
|
||||||
|
|
||||||
|
/* Check wether this is a Sun Audio, .au file */
|
||||||
|
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||||
|
|
||||||
|
if(o->fd!=0) {
|
||||||
|
ret= lseek(o->fd,0,SEEK_SET);
|
||||||
|
if(ret==-1)
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
ret= read(o->fd, buf, 24);
|
||||||
|
if(ret<24)
|
||||||
|
return(0);
|
||||||
|
|
||||||
|
if(strncmp(buf,".snd",4)!=0)
|
||||||
|
return(0);
|
||||||
|
strcpy(o->fmt,".au");
|
||||||
|
o->msb_first= 1;
|
||||||
|
o->au_data_location= libdax_audioxtr_to_int(o,(unsigned char *)buf+4,4,1);
|
||||||
|
o->au_data_size= libdax_audioxtr_to_int(o,(unsigned char *)buf+8,4,1);
|
||||||
|
encoding= libdax_audioxtr_to_int(o,(unsigned char *)buf+12,4,1);
|
||||||
|
if(encoding==2)
|
||||||
|
o->bits_per_sample= 8;
|
||||||
|
else if(encoding==3)
|
||||||
|
o->bits_per_sample= 16;
|
||||||
|
else if(encoding==4)
|
||||||
|
o->bits_per_sample= 24;
|
||||||
|
else if(encoding==5)
|
||||||
|
o->bits_per_sample= 32;
|
||||||
|
else
|
||||||
|
o->bits_per_sample= -encoding;
|
||||||
|
o->sample_rate= libdax_audioxtr_to_int(o,(unsigned char *)buf+16,4,1);
|
||||||
|
o->num_channels= libdax_audioxtr_to_int(o,(unsigned char *)buf+20,4,1);
|
||||||
|
if(o->au_data_size!=0xffffffff)
|
||||||
|
o->data_size= o->au_data_size;
|
||||||
|
else
|
||||||
|
o->data_size= 0;
|
||||||
|
sprintf(o->fmt_info,
|
||||||
|
".au , num_channels=%d , sample_rate=%d , bits_per_sample=%d",
|
||||||
|
o->num_channels,o->sample_rate,o->bits_per_sample);
|
||||||
|
|
||||||
|
/* <<< for testing only */;
|
||||||
|
return(1);
|
||||||
|
|
||||||
|
return(o->bits_per_sample>0); /* Audio format must be linear PCM */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret= libdax_audioxtr_identify_wav(o, 0);
|
||||||
|
if(ret!=0)
|
||||||
|
return(ret);
|
||||||
|
if(o->fd==0) /* cannot rewind stdin */
|
||||||
|
return(0);
|
||||||
|
ret= libdax_audioxtr_identify_au(o, 0);
|
||||||
|
if(ret!=0)
|
||||||
|
return(ret);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* @param flag bit0=msb_first */
|
||||||
|
static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o,
|
||||||
|
unsigned char *bytes, int len, int flag)
|
||||||
|
{
|
||||||
|
unsigned int ret= 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(flag&1)
|
||||||
|
for(i= 0; i<len; i++)
|
||||||
|
ret= ret*256+bytes[i];
|
||||||
|
else
|
||||||
|
for(i= len-1; i>=0; i--)
|
||||||
|
ret= ret*256+bytes[i];
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
|
||||||
|
/* currently this only works for MS WAVE files .wav and Sun .au*/;
|
||||||
|
if(o->fd==0) /* stdin: hope no read came after libdax_audioxtr_identify() */
|
||||||
|
return(1);
|
||||||
|
|
||||||
|
o->extract_count= 0;
|
||||||
|
if(strcmp(o->fmt,".wav")==0)
|
||||||
|
ret= lseek(o->fd,44,SEEK_SET);
|
||||||
|
else if(strcmp(o->fmt,".au")==0)
|
||||||
|
ret= lseek(o->fd,o->au_data_location,SEEK_SET);
|
||||||
|
else
|
||||||
|
ret= -1;
|
||||||
|
if(ret==-1)
|
||||||
|
return(0);
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_get_id(struct libdax_audioxtr *o,
|
||||||
|
char **fmt, char **fmt_info,
|
||||||
|
int *num_channels, int *sample_rate, int *bits_per_sample,
|
||||||
|
int *msb_first, int flag)
|
||||||
|
{
|
||||||
|
*fmt= o->fmt;
|
||||||
|
*fmt_info= o->fmt_info;
|
||||||
|
*num_channels= o->num_channels;
|
||||||
|
*sample_rate= o->sample_rate;
|
||||||
|
*bits_per_sample= o->bits_per_sample;
|
||||||
|
*msb_first= o->msb_first;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag)
|
||||||
|
{
|
||||||
|
*size= o->data_size;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_read(struct libdax_audioxtr *o,
|
||||||
|
char buffer[], int buffer_size, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if(buffer_size<=0 || o->fd<0)
|
||||||
|
return(-2);
|
||||||
|
if(o->data_size>0 && !(flag&1))
|
||||||
|
if(buffer_size > o->data_size - o->extract_count)
|
||||||
|
buffer_size= o->data_size - o->extract_count;
|
||||||
|
if(buffer_size<=0)
|
||||||
|
return(0);
|
||||||
|
ret= read(o->fd,buffer,buffer_size);
|
||||||
|
if(ret>0)
|
||||||
|
o->extract_count+= ret;
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag)
|
||||||
|
{
|
||||||
|
if(o->fd<0)
|
||||||
|
return(-1);
|
||||||
|
if(strcmp(o->fmt,".wav")!=0 && strcmp(o->fmt,".au")!=0)
|
||||||
|
return(0);
|
||||||
|
if(flag&1) {
|
||||||
|
*fd= o->fd;
|
||||||
|
} else {
|
||||||
|
*fd= dup(o->fd);
|
||||||
|
if(*fd>=0 && strcmp(o->path,"-")!=0)
|
||||||
|
close(o->fd);
|
||||||
|
}
|
||||||
|
if(*fd>=0) {
|
||||||
|
o->fd= -1;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
229
libburn/libdax_audioxtr.h
Normal file
229
libburn/libdax_audioxtr.h
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
|
||||||
|
/* libdax_audioxtr
|
||||||
|
Audio track data extraction facility of libdax and libburn.
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LIBDAX_AUDIOXTR_H_INCLUDED
|
||||||
|
#define LIBDAX_AUDIOXTR_H_INCLUDED 1
|
||||||
|
|
||||||
|
/* Public Macros */
|
||||||
|
|
||||||
|
/* Maximum size for address paths and fmt_info strings */
|
||||||
|
#define LIBDAX_AUDIOXTR_STRLEN 4096
|
||||||
|
|
||||||
|
|
||||||
|
/* Public Opaque Handles */
|
||||||
|
|
||||||
|
/** Extractor object encapsulating intermediate states of extraction.
|
||||||
|
The clients of libdax_audioxtr shall only allocate pointers to this
|
||||||
|
struct and get a storage object via libdax_audioxtr_new().
|
||||||
|
Appropriate initial value for the pointer is NULL.
|
||||||
|
*/
|
||||||
|
struct libdax_audioxtr;
|
||||||
|
|
||||||
|
|
||||||
|
/* Public Functions */
|
||||||
|
|
||||||
|
/* Calls initiated from inside libdax/libburn */
|
||||||
|
|
||||||
|
|
||||||
|
/* Calls from applications (to be forwarded by libdax/libburn) */
|
||||||
|
|
||||||
|
|
||||||
|
/** Open an audio file, check wether suitable, create extractor object.
|
||||||
|
@param xtr Opaque handle to extractor. Gets attached extractor object.
|
||||||
|
@param path Address of the audio file to extract. "-" is stdin (but might
|
||||||
|
be not suitable for all futurely supported formats).
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return >0 success
|
||||||
|
0 unsuitable format
|
||||||
|
-1 severe error
|
||||||
|
-2 path not found
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_new(struct libdax_audioxtr **xtr, char *path, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain identification parameters of opened audio source.
|
||||||
|
@param xtr Opaque handle to extractor
|
||||||
|
@param fmt Gets pointed to the audio file format id text: ".wav" , ".au"
|
||||||
|
@param fmt_info Gets pointed to a format info text telling parameters
|
||||||
|
@param num_channels e.g. 1=mono, 2=stereo, etc
|
||||||
|
@param sample_rate e.g. 11025, 44100
|
||||||
|
@param bits_per_sample e.g. 8= 8 bits per sample, 16= 16 bits ...
|
||||||
|
@param msb_first Byte order of samples: 0=Intel 1=Motorola
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return >0 success, <=0 failure
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_get_id(struct libdax_audioxtr *xtr,
|
||||||
|
char **fmt, char **fmt_info,
|
||||||
|
int *num_channels, int *sample_rate,
|
||||||
|
int *bits_per_sample, int *msb_first, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain a prediction about the extracted size based on internal information
|
||||||
|
of the formatted file.
|
||||||
|
@param xtr Opaque handle to extractor
|
||||||
|
@param size Gets filled with the predicted size
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 prediction was possible , 0 no prediction could be made
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_get_size(struct libdax_audioxtr *o, off_t *size, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain next buffer full of extracted data in desired format (only raw audio
|
||||||
|
for now).
|
||||||
|
@param xtr Opaque handle to extractor
|
||||||
|
@param buffer Gets filled with extracted data
|
||||||
|
@param buffer_size Maximum number of bytes to be filled into buffer
|
||||||
|
@param flag Bitfield for control purposes
|
||||||
|
bit0= do not stop at predicted end of data
|
||||||
|
@return >0 number of valid buffer bytes,
|
||||||
|
0 End of file
|
||||||
|
-1 operating system reports error
|
||||||
|
-2 usage error by application
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_read(struct libdax_audioxtr *xtr,
|
||||||
|
char buffer[], int buffer_size, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Try to obtain a file descriptor which will deliver extracted data
|
||||||
|
to normal calls of read(2). This may fail because the format is
|
||||||
|
unsuitable for that, but ".wav" is ok. If this call succeeds the xtr
|
||||||
|
object will have forgotten its file descriptor and libdax_audioxtr_read()
|
||||||
|
will return a usage error. One may use *fd after libdax_audioxtr_destroy()
|
||||||
|
and will have to close it via close(2) when done with it.
|
||||||
|
@param xtr Opaque handle to extractor
|
||||||
|
@param fd Eventually returns the file descriptor number
|
||||||
|
@param flag Bitfield for control purposes
|
||||||
|
bit0= do not dup(2) and close(2) but hand out original fd
|
||||||
|
@return 1 success, 0 cannot hand out fd , -1 severe error
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_detach_fd(struct libdax_audioxtr *o, int *fd, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Clean up after extraction and destroy extractor object.
|
||||||
|
@param xtr Opaque handle to extractor, *xtr is allowed to be NULL,
|
||||||
|
*xtr is set to NULL by this function
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 = destroyed object, 0 = was already destroyed
|
||||||
|
*/
|
||||||
|
int libdax_audioxtr_destroy(struct libdax_audioxtr **xtr, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LIDBAX_AUDIOXTR________________
|
||||||
|
|
||||||
|
|
||||||
|
-- place documentation text here ---
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIDBAX_AUDIOXTR_________________ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*Never* set this macro outside libdax_audioxtr.c !
|
||||||
|
The entrails of this facility are not to be seen by
|
||||||
|
the other library components or the applications.
|
||||||
|
*/
|
||||||
|
#ifdef LIBDAX_AUDIOXTR_H_INTERNAL
|
||||||
|
|
||||||
|
/* Internal Structures */
|
||||||
|
|
||||||
|
/** Extractor object encapsulating intermediate states of extraction */
|
||||||
|
struct libdax_audioxtr {
|
||||||
|
|
||||||
|
/* Source of the encoded audio data */
|
||||||
|
char path[LIBDAX_AUDIOXTR_STRLEN];
|
||||||
|
|
||||||
|
/* File descriptor to path. Anything else than 0 must be lseek-able */
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/* Format identifier. E.g. ".wav" */
|
||||||
|
char fmt[80];
|
||||||
|
|
||||||
|
/* Format parameter info text */
|
||||||
|
char fmt_info[LIBDAX_AUDIOXTR_STRLEN];
|
||||||
|
|
||||||
|
/* 1= mono, 2= stereo, etc. */
|
||||||
|
int num_channels;
|
||||||
|
|
||||||
|
/* 8000, 44100, etc. */
|
||||||
|
int sample_rate;
|
||||||
|
|
||||||
|
/* 8 bits = 8, 16 bits = 16, etc. */
|
||||||
|
int bits_per_sample;
|
||||||
|
|
||||||
|
/* Byte order of samples: 0=Intel 1=Motorola */
|
||||||
|
int msb_first;
|
||||||
|
|
||||||
|
/* Number of bytes to extract (0= unknown/unlimited) */
|
||||||
|
off_t data_size;
|
||||||
|
|
||||||
|
/* Number of extracted data bytes */
|
||||||
|
off_t extract_count;
|
||||||
|
|
||||||
|
|
||||||
|
/* Format dependent parameters */
|
||||||
|
|
||||||
|
/* MS WAVE Format */
|
||||||
|
/* info used: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
|
||||||
|
|
||||||
|
/* == NumSamples * NumChannels * BitsPerSample/8
|
||||||
|
This is the number of bytes in the data. */
|
||||||
|
unsigned wav_subchunk2_size;
|
||||||
|
|
||||||
|
|
||||||
|
/* Sun Audio, .au */
|
||||||
|
/* info used: http://www.opengroup.org/public/pubs/external/auformat.html */
|
||||||
|
|
||||||
|
/* Number of bytes in non-payload header part */
|
||||||
|
unsigned au_data_location;
|
||||||
|
|
||||||
|
/* Number of payload bytes or 0xffffffff */
|
||||||
|
unsigned au_data_size;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Internal Functions */
|
||||||
|
|
||||||
|
/** Open the audio source pointed to by .path and evaluate suitability.
|
||||||
|
@return -1 failure to open, 0 unsuitable format, 1 success
|
||||||
|
*/
|
||||||
|
static int libdax_audioxtr_open(struct libdax_audioxtr *o, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Identify format and evaluate suitability.
|
||||||
|
@return 0 unsuitable format, 1 format is suitable
|
||||||
|
*/
|
||||||
|
static int libdax_audioxtr_identify(struct libdax_audioxtr *o, int flag);
|
||||||
|
|
||||||
|
/** Specialized identifier for .wav */
|
||||||
|
static int libdax_audioxtr_identify_wav(struct libdax_audioxtr *o, int flag);
|
||||||
|
/** Specialized identifier for .au */
|
||||||
|
static int libdax_audioxtr_identify_au(struct libdax_audioxtr *o, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert a byte string into a number (currently only little endian)
|
||||||
|
@param flag Bitfield for control purposes
|
||||||
|
bit0=msb_first
|
||||||
|
@return The resulting number
|
||||||
|
*/
|
||||||
|
static unsigned libdax_audioxtr_to_int(struct libdax_audioxtr *o,
|
||||||
|
unsigned char *bytes, int len, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Prepare for reading of first buffer.
|
||||||
|
@return 0 error, 1 success
|
||||||
|
*/
|
||||||
|
static int libdax_audioxtr_init_reading(struct libdax_audioxtr *o, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIBDAX_AUDIOXTR_H_INTERNAL */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ! LIBDAX_AUDIOXTR_H_INCLUDED */
|
||||||
|
|
404
libburn/libdax_msgs.c
Normal file
404
libburn/libdax_msgs.c
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
|
||||||
|
/* libdax_msgs
|
||||||
|
Message handling facility of libdax.
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/* Only this single source module is entitled to do this */
|
||||||
|
#define LIBDAX_MSGS_H_INTERNAL 1
|
||||||
|
|
||||||
|
/* All participants in the messaging system must do this */
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------- libdax_msgs_item ------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_msgs_item_new(struct libdax_msgs_item **item,
|
||||||
|
struct libdax_msgs_item *link, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct libdax_msgs_item *o;
|
||||||
|
struct timeval tv;
|
||||||
|
struct timezone tz;
|
||||||
|
|
||||||
|
(*item)= o=
|
||||||
|
(struct libdax_msgs_item *) malloc(sizeof(struct libdax_msgs_item));
|
||||||
|
if(o==NULL)
|
||||||
|
return(-1);
|
||||||
|
o->timestamp= 0.0;
|
||||||
|
ret= gettimeofday(&tv,&tz);
|
||||||
|
if(ret==0)
|
||||||
|
o->timestamp= tv.tv_sec+0.000001*tv.tv_usec;
|
||||||
|
o->process_id= getpid();
|
||||||
|
o->driveno= -1;
|
||||||
|
o->severity= LIBDAX_MSGS_SEV_ALL;
|
||||||
|
o->priority= LIBDAX_MSGS_PRIO_ZERO;
|
||||||
|
o->error_code= 0;
|
||||||
|
o->msg_text= NULL;
|
||||||
|
o->os_errno= 0;
|
||||||
|
o->prev= link;
|
||||||
|
o->next= NULL;
|
||||||
|
if(link!=NULL) {
|
||||||
|
if(link->next!=NULL) {
|
||||||
|
link->next->prev= o;
|
||||||
|
o->next= link->next;
|
||||||
|
}
|
||||||
|
link->next= o;
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Detaches item from its queue and eventually readjusts start, end pointers
|
||||||
|
of the queue */
|
||||||
|
int libdax_msgs_item_unlink(struct libdax_msgs_item *o,
|
||||||
|
struct libdax_msgs_item **chain_start,
|
||||||
|
struct libdax_msgs_item **chain_end, int flag)
|
||||||
|
{
|
||||||
|
if(o->prev!=NULL)
|
||||||
|
o->prev->next= o->next;
|
||||||
|
if(o->next!=NULL)
|
||||||
|
o->next->prev= o->prev;
|
||||||
|
if(chain_start!=NULL)
|
||||||
|
if(*chain_start == o)
|
||||||
|
*chain_start= o->next;
|
||||||
|
if(chain_end!=NULL)
|
||||||
|
if(*chain_end == o)
|
||||||
|
*chain_end= o->prev;
|
||||||
|
o->next= o->prev= NULL;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_item_destroy(struct libdax_msgs_item **item,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
struct libdax_msgs_item *o;
|
||||||
|
|
||||||
|
o= *item;
|
||||||
|
if(o==NULL)
|
||||||
|
return(0);
|
||||||
|
libdax_msgs_item_unlink(o,NULL,NULL,0);
|
||||||
|
if(o->msg_text!=NULL)
|
||||||
|
free((char *) o->msg_text);
|
||||||
|
free((char *) o);
|
||||||
|
*item= NULL;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_item_get_msg(struct libdax_msgs_item *item,
|
||||||
|
int *error_code, char **msg_text, int *os_errno,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
*error_code= item->error_code;
|
||||||
|
*msg_text= item->msg_text;
|
||||||
|
*os_errno= item->os_errno;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_item_get_origin(struct libdax_msgs_item *item,
|
||||||
|
double *timestamp, pid_t *process_id, int *driveno,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
*timestamp= item->timestamp;
|
||||||
|
*process_id= item->process_id;
|
||||||
|
*driveno= item->driveno;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_item_get_rank(struct libdax_msgs_item *item,
|
||||||
|
int *severity, int *priority, int flag)
|
||||||
|
{
|
||||||
|
*severity= item->severity;
|
||||||
|
*priority= item->priority;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------- libdax_msgs ---------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_new(struct libdax_msgs **m, int flag)
|
||||||
|
{
|
||||||
|
struct libdax_msgs *o;
|
||||||
|
|
||||||
|
(*m)= o= (struct libdax_msgs *) malloc(sizeof(struct libdax_msgs));
|
||||||
|
if(o==NULL)
|
||||||
|
return(-1);
|
||||||
|
o->oldest= NULL;
|
||||||
|
o->youngest= NULL;
|
||||||
|
o->count= 0;
|
||||||
|
o->queue_severity= LIBDAX_MSGS_SEV_ALL;
|
||||||
|
o->print_severity= LIBDAX_MSGS_SEV_NEVER;
|
||||||
|
strcpy(o->print_id,"libdax: ");
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
pthread_mutex_init(&(o->lock_mutex),NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_destroy(struct libdax_msgs **m, int flag)
|
||||||
|
{
|
||||||
|
struct libdax_msgs *o;
|
||||||
|
struct libdax_msgs_item *item, *next_item;
|
||||||
|
|
||||||
|
o= *m;
|
||||||
|
if(o==NULL)
|
||||||
|
return(0);
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
if(pthread_mutex_destroy(&(o->lock_mutex))!=0) {
|
||||||
|
pthread_mutex_unlock(&(o->lock_mutex));
|
||||||
|
pthread_mutex_destroy(&(o->lock_mutex));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for(item= o->oldest; item!=NULL; item= next_item) {
|
||||||
|
next_item= item->next;
|
||||||
|
libdax_msgs_item_destroy(&item,0);
|
||||||
|
}
|
||||||
|
free((char *) o);
|
||||||
|
*m= NULL;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity,
|
||||||
|
int print_severity, char *print_id, int flag)
|
||||||
|
{
|
||||||
|
m->queue_severity= queue_severity;
|
||||||
|
m->print_severity= print_severity;
|
||||||
|
strncpy(m->print_id,print_id,80);
|
||||||
|
m->print_id[80]= 0;
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_msgs_lock(struct libdax_msgs *m, int flag)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret= pthread_mutex_lock(&(m->lock_mutex));
|
||||||
|
if(ret!=0)
|
||||||
|
return(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int libdax_msgs_unlock(struct libdax_msgs *m, int flag)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret= pthread_mutex_unlock(&(m->lock_mutex));
|
||||||
|
if(ret!=0)
|
||||||
|
return(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs__text_to_sev(char *severity_name, int *severity,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
if(strncmp(severity_name,"NEVER",5)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_NEVER;
|
||||||
|
else if(strncmp(severity_name,"ABORT",5)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_ABORT;
|
||||||
|
else if(strncmp(severity_name,"FATAL",5)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_FATAL;
|
||||||
|
else if(strncmp(severity_name,"SORRY",5)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_SORRY;
|
||||||
|
else if(strncmp(severity_name,"WARNING",7)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_WARNING;
|
||||||
|
else if(strncmp(severity_name,"HINT",4)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_HINT;
|
||||||
|
else if(strncmp(severity_name,"NOTE",4)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_NOTE;
|
||||||
|
else if(strncmp(severity_name,"UPDATE",6)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_UPDATE;
|
||||||
|
else if(strncmp(severity_name,"DEBUG",5)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_DEBUG;
|
||||||
|
else if(strncmp(severity_name,"ALL",3)==0)
|
||||||
|
*severity= LIBDAX_MSGS_SEV_ALL;
|
||||||
|
else {
|
||||||
|
*severity= LIBDAX_MSGS_SEV_NEVER;
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs__sev_to_text(int severity, char **severity_name,
|
||||||
|
int flag)
|
||||||
|
{
|
||||||
|
if(flag&1) {
|
||||||
|
*severity_name=
|
||||||
|
"NEVER\nABORT\nFATAL\nSORRY\nWARNING\nHINT\nNOTE\nUPDATE\nDEBUG\nALL";
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
*severity_name= "";
|
||||||
|
if(severity>=LIBDAX_MSGS_SEV_NEVER)
|
||||||
|
*severity_name= "NEVER";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_ABORT)
|
||||||
|
*severity_name= "ABORT";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_FATAL)
|
||||||
|
*severity_name= "FATAL";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_SORRY)
|
||||||
|
*severity_name= "SORRY";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_WARNING)
|
||||||
|
*severity_name= "WARNING";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_HINT)
|
||||||
|
*severity_name= "HINT";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_NOTE)
|
||||||
|
*severity_name= "NOTE";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_UPDATE)
|
||||||
|
*severity_name= "UPDATE";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_DEBUG)
|
||||||
|
*severity_name= "DEBUG";
|
||||||
|
else if(severity>=LIBDAX_MSGS_SEV_ALL)
|
||||||
|
*severity_name= "ALL";
|
||||||
|
else {
|
||||||
|
*severity_name= "";
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code,
|
||||||
|
int severity, int priority, char *msg_text,
|
||||||
|
int os_errno, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *textpt,*sev_name,sev_text[81];
|
||||||
|
struct libdax_msgs_item *item= NULL;
|
||||||
|
|
||||||
|
if(severity >= m->print_severity) {
|
||||||
|
if(msg_text==NULL)
|
||||||
|
textpt= "";
|
||||||
|
else
|
||||||
|
textpt= msg_text;
|
||||||
|
sev_text[0]= 0;
|
||||||
|
ret= libdax_msgs__sev_to_text(severity,&sev_name,0);
|
||||||
|
if(ret>0)
|
||||||
|
sprintf(sev_text,"%s : ",sev_name);
|
||||||
|
|
||||||
|
fprintf(stderr,"%s%s%s\n",m->print_id,sev_text,textpt);
|
||||||
|
if(os_errno!=0) {
|
||||||
|
ret= libdax_msgs_lock(m,0);
|
||||||
|
if(ret<=0)
|
||||||
|
return(-1);
|
||||||
|
fprintf(stderr,"%s( Most recent system error: %d '%s' )\n",
|
||||||
|
m->print_id,os_errno,strerror(os_errno));
|
||||||
|
libdax_msgs_unlock(m,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(severity < m->queue_severity)
|
||||||
|
return(0);
|
||||||
|
|
||||||
|
ret= libdax_msgs_lock(m,0);
|
||||||
|
if(ret<=0)
|
||||||
|
return(-1);
|
||||||
|
ret= libdax_msgs_item_new(&item,m->youngest,0);
|
||||||
|
if(ret<=0)
|
||||||
|
goto failed;
|
||||||
|
item->driveno= driveno;
|
||||||
|
item->error_code= error_code;
|
||||||
|
item->severity= severity;
|
||||||
|
item->priority= priority;
|
||||||
|
if(msg_text!=NULL) {
|
||||||
|
item->msg_text= malloc(strlen(msg_text)+1);
|
||||||
|
if(item->msg_text==NULL)
|
||||||
|
goto failed;
|
||||||
|
strcpy(item->msg_text,msg_text);
|
||||||
|
}
|
||||||
|
item->os_errno= os_errno;
|
||||||
|
if(m->oldest==NULL)
|
||||||
|
m->oldest= item;
|
||||||
|
m->youngest= item;
|
||||||
|
m->count++;
|
||||||
|
libdax_msgs_unlock(m,0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
fprintf(stderr,"libdax_experimental: message submitted to queue (now %d)\n",
|
||||||
|
m->count);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return(1);
|
||||||
|
failed:;
|
||||||
|
libdax_msgs_item_destroy(&item,0);
|
||||||
|
libdax_msgs_unlock(m,0);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item,
|
||||||
|
int severity, int priority, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct libdax_msgs_item *im, *next_im= NULL;
|
||||||
|
|
||||||
|
*item= NULL;
|
||||||
|
ret= libdax_msgs_lock(m,0);
|
||||||
|
if(ret<=0)
|
||||||
|
return(-1);
|
||||||
|
for(im= m->oldest; im!=NULL; im= next_im) {
|
||||||
|
for(; im!=NULL; im= next_im) {
|
||||||
|
next_im= im->next;
|
||||||
|
if(im->severity>=severity)
|
||||||
|
break;
|
||||||
|
libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
|
||||||
|
libdax_msgs_item_destroy(&im,0); /* severity too low: delete */
|
||||||
|
}
|
||||||
|
if(im==NULL)
|
||||||
|
break;
|
||||||
|
if(im->priority>=priority)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(im==NULL)
|
||||||
|
{ret= 0; goto ex;}
|
||||||
|
libdax_msgs_item_unlink(im,&(m->oldest),&(m->youngest),0);
|
||||||
|
*item= im;
|
||||||
|
ret= 1;
|
||||||
|
ex:;
|
||||||
|
libdax_msgs_unlock(m,0);
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int libdax_msgs_destroy_item(struct libdax_msgs *m,
|
||||||
|
struct libdax_msgs_item **item, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret= libdax_msgs_lock(m,0);
|
||||||
|
if(ret<=0)
|
||||||
|
return(-1);
|
||||||
|
ret= libdax_msgs_item_destroy(item,0);
|
||||||
|
libdax_msgs_unlock(m,0);
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
408
libburn/libdax_msgs.h
Normal file
408
libburn/libdax_msgs.h
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
|
||||||
|
/* libdax_msgs
|
||||||
|
Message handling facility of libdax.
|
||||||
|
Copyright (C) 2006-2007 Thomas Schmitt <scdbackup@gmx.net>,
|
||||||
|
provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*Never* set this macro outside libdax_msgs.c !
|
||||||
|
The entrails of the message handling facility are not to be seen by
|
||||||
|
the other library components or the applications.
|
||||||
|
*/
|
||||||
|
#ifdef LIBDAX_MSGS_H_INTERNAL
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct libdax_msgs_item {
|
||||||
|
|
||||||
|
double timestamp;
|
||||||
|
pid_t process_id;
|
||||||
|
int driveno;
|
||||||
|
|
||||||
|
int severity;
|
||||||
|
int priority;
|
||||||
|
|
||||||
|
/* Apply for your developer's error code range at
|
||||||
|
libburn-hackers@pykix.org
|
||||||
|
Report introduced codes in the list below. */
|
||||||
|
int error_code;
|
||||||
|
|
||||||
|
char *msg_text;
|
||||||
|
int os_errno;
|
||||||
|
|
||||||
|
struct libdax_msgs_item *prev,*next;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct libdax_msgs {
|
||||||
|
|
||||||
|
struct libdax_msgs_item *oldest;
|
||||||
|
struct libdax_msgs_item *youngest;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
int queue_severity;
|
||||||
|
int print_severity;
|
||||||
|
char print_id[81];
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_SINGLE_THREADED
|
||||||
|
pthread_mutex_t lock_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* LIBDAX_MSGS_H_INTERNAL */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_H_INCLUDED
|
||||||
|
#define LIBDAX_MSGS_H_INCLUDED 1
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIBDAX_MSGS_H_INTERNAL
|
||||||
|
|
||||||
|
|
||||||
|
/* Public Opaque Handles */
|
||||||
|
|
||||||
|
/** A pointer to this is a opaque handle to a message handling facility */
|
||||||
|
struct libdax_msgs;
|
||||||
|
|
||||||
|
/** A pointer to this is a opaque handle to a single message item */
|
||||||
|
struct libdax_msgs_item;
|
||||||
|
|
||||||
|
#endif /* ! LIBDAX_MSGS_H_INTERNAL */
|
||||||
|
|
||||||
|
|
||||||
|
/* Public Macros */
|
||||||
|
|
||||||
|
|
||||||
|
/* Registered Severities */
|
||||||
|
|
||||||
|
/* It is well advisable to let applications select severities via strings and
|
||||||
|
forwarded functions libdax_msgs__text_to_sev(), libdax_msgs__sev_to_text().
|
||||||
|
These macros are for use by libdax/libburn only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Use this to get messages of any severity. Do not use for submitting.
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_ALL 0x00000000
|
||||||
|
|
||||||
|
/** Debugging messages not to be visible to normal users by default
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_DEBUG 0x10000000
|
||||||
|
|
||||||
|
/** Update of a progress report about long running actions
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_UPDATE 0x20000000
|
||||||
|
|
||||||
|
/** Not so usual events which were gracefully handled
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_NOTE 0x30000000
|
||||||
|
|
||||||
|
/** Possibilities to achieve a better result
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_HINT 0x40000000
|
||||||
|
|
||||||
|
/** Warnings about problems which could not be handled optimally
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_WARNING 0x50000000
|
||||||
|
|
||||||
|
/** Non-fatal error messages indicating that parts of the action failed
|
||||||
|
but processing will/should go on
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_SORRY 0x60000000
|
||||||
|
|
||||||
|
/** An error message which puts the whole operation of libdax in question
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_FATAL 0x70000000
|
||||||
|
|
||||||
|
/** A message from an abort handler which will finally finish libburn
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_ABORT 0x71000000
|
||||||
|
|
||||||
|
/** A severity to exclude resp. discard any possible message.
|
||||||
|
Do not use this severity for submitting.
|
||||||
|
*/
|
||||||
|
#define LIBDAX_MSGS_SEV_NEVER 0x7fffffff
|
||||||
|
|
||||||
|
|
||||||
|
/* Registered Priorities */
|
||||||
|
|
||||||
|
/* Priorities are to be used by libburn/libdax only. */
|
||||||
|
|
||||||
|
#define LIBDAX_MSGS_PRIO_ZERO 0x00000000
|
||||||
|
#define LIBDAX_MSGS_PRIO_LOW 0x10000000
|
||||||
|
#define LIBDAX_MSGS_PRIO_MEDIUM 0x20000000
|
||||||
|
#define LIBDAX_MSGS_PRIO_HIGH 0x30000000
|
||||||
|
#define LIBDAX_MSGS_PRIO_TOP 0x7ffffffe
|
||||||
|
|
||||||
|
/* Do not use this priority for submitting */
|
||||||
|
#define LIBDAX_MSGS_PRIO_NEVER 0x7fffffff
|
||||||
|
|
||||||
|
|
||||||
|
/* Public Functions */
|
||||||
|
|
||||||
|
/* Calls initiated from inside libdax/libburn */
|
||||||
|
|
||||||
|
|
||||||
|
/** Create new empty message handling facility with queue.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return >0 success, <=0 failure
|
||||||
|
*/
|
||||||
|
int libdax_msgs_new(struct libdax_msgs **m, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Destroy a message handling facility and all its eventual messages.
|
||||||
|
The submitted pointer gets set to NULL.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 for success, 0 for pointer to NULL
|
||||||
|
*/
|
||||||
|
int libdax_msgs_destroy(struct libdax_msgs **m, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Submit a message to a message handling facility.
|
||||||
|
@param driveno libdax drive number. Use -1 if no number is known.
|
||||||
|
@param error_code Unique error code. Use only registered codes. See below.
|
||||||
|
The same unique error_code may be issued at different
|
||||||
|
occasions but those should be equivalent out of the view
|
||||||
|
of a libdax application. (E.g. "cannot open ATA drive"
|
||||||
|
versus "cannot open SCSI drive" would be equivalent.)
|
||||||
|
@param severity The LIBDAX_MSGS_SEV_* of the event.
|
||||||
|
@param priority The LIBDAX_MSGS_PRIO_* number of the event.
|
||||||
|
@param msg_text Printable and human readable message text.
|
||||||
|
@param os_errno Eventual error code from operating system (0 if none)
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 on success, 0 on rejection, <0 for severe errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_submit(struct libdax_msgs *m, int driveno, int error_code,
|
||||||
|
int severity, int priority, char *msg_text,
|
||||||
|
int os_errno, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/* Calls from applications (to be forwarded by libdax/libburn) */
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert a registered severity number into a severity name
|
||||||
|
@param flag Bitfield for control purposes:
|
||||||
|
bit0= list all severity names in a newline separated string
|
||||||
|
@return >0 success, <=0 failure
|
||||||
|
*/
|
||||||
|
int libdax_msgs__sev_to_text(int severity, char **severity_name,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Convert a severity name into a severity number,
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return >0 success, <=0 failure
|
||||||
|
*/
|
||||||
|
int libdax_msgs__text_to_sev(char *severity_name, int *severity,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Set minimum severity for messages to be queued (default
|
||||||
|
LIBDAX_MSGS_SEV_ALL) and for messages to be printed directly to stderr
|
||||||
|
(default LIBDAX_MSGS_SEV_NEVER).
|
||||||
|
@param print_id A text of at most 80 characters to be printed before
|
||||||
|
any eventually printed message (default is "libdax: ").
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return always 1 for now
|
||||||
|
*/
|
||||||
|
int libdax_msgs_set_severities(struct libdax_msgs *m, int queue_severity,
|
||||||
|
int print_severity, char *print_id, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain a message item that has at least the given severity and priority.
|
||||||
|
Usually all older messages of lower severity are discarded then. If no
|
||||||
|
item of sufficient severity was found, all others are discarded from the
|
||||||
|
queue.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 if a matching item was found, 0 if not, <0 for severe errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_obtain(struct libdax_msgs *m, struct libdax_msgs_item **item,
|
||||||
|
int severity, int priority, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted
|
||||||
|
pointer gets set to NULL.
|
||||||
|
Caution: Copy eventually obtained msg_text before destroying the item,
|
||||||
|
if you want to use it further.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 for success, 0 for pointer to NULL, <0 for severe errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_destroy_item(struct libdax_msgs *m,
|
||||||
|
struct libdax_msgs_item **item, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain from a message item the three application oriented components as
|
||||||
|
submitted with the originating call of libdax_msgs_submit().
|
||||||
|
Caution: msg_text becomes a pointer into item, not a copy.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_item_get_msg(struct libdax_msgs_item *item,
|
||||||
|
int *error_code, char **msg_text, int *os_errno,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain from a message item the submitter identification submitted
|
||||||
|
with the originating call of libdax_msgs_submit().
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_item_get_origin(struct libdax_msgs_item *item,
|
||||||
|
double *timestamp, pid_t *process_id, int *driveno,
|
||||||
|
int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain from a message item severity and priority as submitted
|
||||||
|
with the originating call of libdax_msgs_submit().
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 on success, 0 on invalid item, <0 for servere errors
|
||||||
|
*/
|
||||||
|
int libdax_msgs_item_get_rank(struct libdax_msgs_item *item,
|
||||||
|
int *severity, int *priority, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LIDBAX_MSGS_________________
|
||||||
|
|
||||||
|
|
||||||
|
/* Registered Error Codes */
|
||||||
|
|
||||||
|
|
||||||
|
Format: error_code (LIBDAX_MSGS_SEV_*,LIBDAX_MSGS_PRIO_*) = explanation
|
||||||
|
If no severity or priority are fixely associates, use "(,)".
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Range "libdax_msgs" : 0x00000000 to 0x0000ffff
|
||||||
|
|
||||||
|
0x00000000 (ALL,ZERO) = Initial setting in new libdax_msgs_item
|
||||||
|
0x00000001 (DEBUG,ZERO) = Test error message
|
||||||
|
0x00000002 (DEBUG,ZERO) = Debugging message
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Range "elmom" : 0x00010000 to 0x0001ffff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Range "scdbackup" : 0x00020000 to 0x0002ffff
|
||||||
|
|
||||||
|
Acessing and defending drives:
|
||||||
|
|
||||||
|
0x00020001 (SORRY,LOW) = Cannot open busy device
|
||||||
|
0x00020002 (SORRY,HIGH) = Encountered error when closing drive
|
||||||
|
0x00020003 (SORRY,HIGH) = Could not grab drive
|
||||||
|
0x00020004 (NOTE,HIGH) = Opened O_EXCL scsi sibling
|
||||||
|
0x00020005 (FATAL,HIGH) = Failed to open device
|
||||||
|
0x00020006 (FATAL,HIGH) = Too many scsi siblings
|
||||||
|
0x00020007 (NOTE,HIGH) = Closed O_EXCL scsi siblings
|
||||||
|
|
||||||
|
General library operations:
|
||||||
|
|
||||||
|
0x00020101 (WARNING,HIGH) = Cannot find given worker item
|
||||||
|
0x00020102 (SORRY,HIGH) = A drive operation is still going on
|
||||||
|
0x00020103 (WARNING,HIGH) = After scan a drive operation is still going on
|
||||||
|
0x00020104 (SORRY,HIGH) = NULL pointer caught
|
||||||
|
0x00020105 (SORRY,HIGH) = Drive is already released
|
||||||
|
0x00020106 (SORRY,HIGH) = Drive is busy on attempt to close
|
||||||
|
0x00020107 (SORRY,HIGH) = Drive is busy on attempt to shut down library
|
||||||
|
0x00020108 (SORRY,HIGH) = Drive is not grabbed on disc status inquiry
|
||||||
|
0x00020108 (FATAL,HIGH) = Could not allocate new drive object
|
||||||
|
0x00020109 (FATAL,HIGH) = Library not running
|
||||||
|
0x0002010a (FATAL,HIGH) = Unsuitable track mode
|
||||||
|
0x0002010b (FATAL,HIGH) = Burn run failed
|
||||||
|
0x0002010c (FATAL,HIGH) = Failed to transfer command to drive
|
||||||
|
0x0002010d (DEBUG,HIGH) = Could not inquire TOC
|
||||||
|
0x0002010e (FATAL,HIGH) = Attempt to read ATIP from ungrabbed drive
|
||||||
|
0x0002010f (DEBUG,HIGH) = SCSI error condition on command
|
||||||
|
0x00020110 (FATAL,HIGH) = Persistent drive address too long
|
||||||
|
0x00020111 (FATAL,HIGH) = Could not allocate new auxiliary object
|
||||||
|
0x00020112 (SORRY,HIGH) = Bad combination of write_type and block_type
|
||||||
|
0x00020113 (FATAL,HIGH) = Drive capabilities not inquired yet
|
||||||
|
0x00020114 (SORRY,HIGH) = Attempt to set ISRC with bad data
|
||||||
|
0x00020115 (SORRY,HIGH) = Attempt to set track mode to unusable value
|
||||||
|
0x00020116 (FATAL,HIGH) = Track mode has unusable value
|
||||||
|
0x00020117 (FATAL,HIGH) = toc_entry of drive is already in use
|
||||||
|
0x00020118 (DEBUG,HIGH) = Closing track
|
||||||
|
0x00020119 (DEBUG,HIGH) = Closing session
|
||||||
|
0x0002011a (NOTE,HIGH) = Padding up track to minimum size
|
||||||
|
0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive
|
||||||
|
0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive
|
||||||
|
0x0002011d (FATAL,HIGH) = SCSI error on write
|
||||||
|
0x0002011e (SORRY,HIGH) = Unsuitable media detected
|
||||||
|
0x0002011f (SORRY,HIGH) = Burning is restricted to a single track
|
||||||
|
0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored
|
||||||
|
0x00020121 (FATAL,HIGH) = Write preparation setup failed
|
||||||
|
0x00020122 (FATAL,HIGH) = SCSI error on format_unit
|
||||||
|
0x00020123 (SORRY,HIGH) = DVD Media are unsuitable for desired track type
|
||||||
|
0x00020124 (SORRY,HIGH) = SCSI error on set_streaming
|
||||||
|
0x00020125 (SORRY,HIGH) = Write start address not supported
|
||||||
|
0x00020126 (SORRY,HIGH) = Write start address not properly aligned
|
||||||
|
0x00020127 (NOTE,HIGH) = Write start address is ...
|
||||||
|
0x00020128 (FATAL,HIGH) = Unsupported inquiry_type with mmc_get_performance
|
||||||
|
0x00020129 (SORRY,HIGH) = Will not format media type
|
||||||
|
0x0002012a (FATAL,HIGH) = Cannot inquire write mode capabilities
|
||||||
|
0x0002012b (FATAL,HIGH) = Drive offers no suitable write mode with this job
|
||||||
|
|
||||||
|
0x00020130 (SORRY,HIGH) = Drive and media state unsuitable for blanking
|
||||||
|
0x00020131 (SORRY,HIGH) = No suitable formatting type offered by drive
|
||||||
|
0x00020132 (SORRY,HIGH) = Selected format is not suitable for libburn
|
||||||
|
0x00020133 (SORRY,HIGH) = Cannot mix data and audio in SAO mode
|
||||||
|
0x00020134 (NOTE,HIGH) = Defaulted TAO to DAO
|
||||||
|
0x00020135 (SORRY,HIGH) = Cannot perform TAO, job unsuitable for DAO
|
||||||
|
0x00020136 (SORRY,HIGH) = DAO Burning restricted to single fixed size track
|
||||||
|
0x00020137 (HINT,HIGH) = TAO would be possible
|
||||||
|
0x00020138 (FATAL,HIGH) = Cannot reserve track
|
||||||
|
|
||||||
|
|
||||||
|
libdax_audioxtr:
|
||||||
|
0x00020200 (SORRY,HIGH) = Cannot open audio source file
|
||||||
|
0x00020201 (SORRY,HIGH) = Audio source file has unsuitable format
|
||||||
|
0x00020202 (SORRY,HIGH) = Failed to prepare reading of audio data
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#endif /* LIDBAX_MSGS_________________ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LIBDAX_MSGS_H_INTERNAL
|
||||||
|
|
||||||
|
/* Internal Functions */
|
||||||
|
|
||||||
|
|
||||||
|
/** Lock before doing side effect operations on m */
|
||||||
|
static int libdax_msgs_lock(struct libdax_msgs *m, int flag);
|
||||||
|
|
||||||
|
/** Unlock after effect operations on m are done */
|
||||||
|
static int libdax_msgs_unlock(struct libdax_msgs *m, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
/** Create new empty message item.
|
||||||
|
@param link Previous item in queue
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return >0 success, <=0 failure
|
||||||
|
*/
|
||||||
|
static int libdax_msgs_item_new(struct libdax_msgs_item **item,
|
||||||
|
struct libdax_msgs_item *link, int flag);
|
||||||
|
|
||||||
|
/** Destroy a message item obtained by libdax_msgs_obtain(). The submitted
|
||||||
|
pointer gets set to NULL.
|
||||||
|
@param flag Bitfield for control purposes (unused yet, submit 0)
|
||||||
|
@return 1 for success, 0 for pointer to NULL
|
||||||
|
*/
|
||||||
|
static int libdax_msgs_item_destroy(struct libdax_msgs_item **item, int flag);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIBDAX_MSGS_H_INTERNAL */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ! LIBDAX_MSGS_H_INCLUDED */
|
@ -1,108 +0,0 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
|
||||||
|
|
||||||
#include "message.h"
|
|
||||||
#include "libburn.h"
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
struct message_list
|
|
||||||
{
|
|
||||||
struct message_list *next;
|
|
||||||
|
|
||||||
struct burn_message *msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct message_list *queue;
|
|
||||||
|
|
||||||
void burn_message_free(struct burn_message *msg)
|
|
||||||
{
|
|
||||||
free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void burn_message_clear_queue(void)
|
|
||||||
{
|
|
||||||
struct burn_message *msg;
|
|
||||||
|
|
||||||
if ((msg = burn_get_message())) {
|
|
||||||
burn_print(0,
|
|
||||||
"YOU HAVE MESSAGES QUEUED FROM THE LAST OPERATION. "
|
|
||||||
"YOU SHOULD BE GRABBING THEM ALL!\n");
|
|
||||||
do {
|
|
||||||
burn_message_free(msg);
|
|
||||||
} while ((msg = burn_get_message()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct burn_message *burn_get_message()
|
|
||||||
{
|
|
||||||
struct burn_message *msg = NULL;
|
|
||||||
|
|
||||||
if (queue) {
|
|
||||||
struct message_list *next;
|
|
||||||
|
|
||||||
next = queue->next;
|
|
||||||
msg = queue->msg;
|
|
||||||
free(queue);
|
|
||||||
queue = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void queue_push_tail(struct burn_message *msg)
|
|
||||||
{
|
|
||||||
struct message_list *node;
|
|
||||||
|
|
||||||
node = malloc(sizeof(struct message_list));
|
|
||||||
node->next = NULL;
|
|
||||||
node->msg = msg;
|
|
||||||
|
|
||||||
if (!queue)
|
|
||||||
queue = node;
|
|
||||||
else {
|
|
||||||
struct message_list *it;
|
|
||||||
|
|
||||||
for (it = queue; it->next; it = it->next) ;
|
|
||||||
it->next = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void burn_message_info_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message)
|
|
||||||
{
|
|
||||||
struct burn_message *msg;
|
|
||||||
|
|
||||||
msg = malloc(sizeof(struct burn_message));
|
|
||||||
msg->drive = drive;
|
|
||||||
msg->type = BURN_MESSAGE_INFO;
|
|
||||||
msg->detail.info.message = message;
|
|
||||||
|
|
||||||
queue_push_tail(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void burn_message_warning_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message)
|
|
||||||
{
|
|
||||||
struct burn_message *msg;
|
|
||||||
|
|
||||||
msg = malloc(sizeof(struct burn_message));
|
|
||||||
msg->drive = drive;
|
|
||||||
msg->type = BURN_MESSAGE_WARNING;
|
|
||||||
msg->detail.warning.message = message;
|
|
||||||
|
|
||||||
queue_push_tail(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void burn_message_error_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message)
|
|
||||||
{
|
|
||||||
struct burn_message *msg;
|
|
||||||
|
|
||||||
msg = malloc(sizeof(struct burn_message));
|
|
||||||
msg->drive = drive;
|
|
||||||
msg->type = BURN_MESSAGE_ERROR;
|
|
||||||
msg->detail.error.message = message;
|
|
||||||
|
|
||||||
queue_push_tail(msg);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
|
||||||
|
|
||||||
#ifndef __MESSAGE
|
|
||||||
#define __MESSAGE
|
|
||||||
|
|
||||||
#include "libburn.h"
|
|
||||||
|
|
||||||
void burn_message_clear_queue(void);
|
|
||||||
|
|
||||||
void burn_message_info_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message);
|
|
||||||
|
|
||||||
void burn_message_warning_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message);
|
|
||||||
|
|
||||||
void burn_message_error_new(struct burn_drive *drive,
|
|
||||||
enum burn_message_info message);
|
|
||||||
|
|
||||||
#endif
|
|
1876
libburn/mmc.c
1876
libburn/mmc.c
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,13 @@ struct cue_sheet;
|
|||||||
/* MMC commands */
|
/* MMC commands */
|
||||||
|
|
||||||
void mmc_read(struct burn_drive *);
|
void mmc_read(struct burn_drive *);
|
||||||
void mmc_close_session(struct burn_drive *, struct burn_write_opts *);
|
|
||||||
void mmc_close_disc(struct burn_drive *, struct burn_write_opts *);
|
/* ts A61009 : removed redundant parameter d in favor of o->drive */
|
||||||
|
/* void mmc_close_session(struct burn_drive *, struct burn_write_opts *); */
|
||||||
|
/* void mmc_close_disc(struct burn_drive *, struct burn_write_opts *); */
|
||||||
|
void mmc_close_session(struct burn_write_opts *o);
|
||||||
|
void mmc_close_disc(struct burn_write_opts *o);
|
||||||
|
|
||||||
void mmc_close(struct burn_drive *, int session, int track);
|
void mmc_close(struct burn_drive *, int session, int track);
|
||||||
void mmc_get_event(struct burn_drive *);
|
void mmc_get_event(struct burn_drive *);
|
||||||
int mmc_write(struct burn_drive *, int start, struct buffer *buf);
|
int mmc_write(struct burn_drive *, int start, struct buffer *buf);
|
||||||
@ -32,6 +37,36 @@ void mmc_set_speed(struct burn_drive *, int, int);
|
|||||||
void mmc_read_lead_in(struct burn_drive *, struct buffer *);
|
void mmc_read_lead_in(struct burn_drive *, struct buffer *);
|
||||||
void mmc_perform_opc(struct burn_drive *);
|
void mmc_perform_opc(struct burn_drive *);
|
||||||
void mmc_get_configuration(struct burn_drive *);
|
void mmc_get_configuration(struct burn_drive *);
|
||||||
int mmc_get_nwa(struct burn_drive *);
|
|
||||||
|
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
|
||||||
|
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
|
||||||
|
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa);
|
||||||
|
|
||||||
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *);
|
||||||
|
|
||||||
|
/* ts A61023 : get size and free space of drive buffer */
|
||||||
|
int mmc_read_buffer_capacity(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61021 : the mmc specific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int mmc_setup_drive(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format()
|
||||||
|
and mmc5r03c.pdf, 6.5 FORMAT UNIT */
|
||||||
|
int mmc_format_unit(struct burn_drive *d, off_t size, int flag);
|
||||||
|
|
||||||
|
/* ts A61225 : obtain write speed descriptors via ACh GET PERFORMANCE */
|
||||||
|
int mmc_get_write_performance(struct burn_drive *d);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61229 : outsourced from spc_select_write_params() */
|
||||||
|
/* Note: Page data is not zeroed here to allow preset defaults. Thus
|
||||||
|
memset(pd, 0, 2 + d->mdata->write_page_length);
|
||||||
|
is the eventual duty of the caller.
|
||||||
|
*/
|
||||||
|
int mmc_compose_mode_page_5(struct burn_drive *d,
|
||||||
|
const struct burn_write_opts *o,
|
||||||
|
unsigned char *pd);
|
||||||
|
|
||||||
|
|
||||||
#endif /*__MMC*/
|
#endif /*__MMC*/
|
||||||
|
@ -21,6 +21,10 @@ struct burn_source *burn_null_source_new(void)
|
|||||||
src->read_sub = NULL;
|
src->read_sub = NULL;
|
||||||
|
|
||||||
src->get_size = 0;
|
src->get_size = 0;
|
||||||
|
|
||||||
|
/* ts A70126 */
|
||||||
|
src->set_size = NULL;
|
||||||
|
|
||||||
src->free_data = NULL;
|
src->free_data = NULL;
|
||||||
src->data = NULL;
|
src->data = NULL;
|
||||||
return src;
|
return src;
|
||||||
|
@ -1,15 +1,29 @@
|
|||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "drive.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
#include <assert.h>
|
/* ts A61007 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
|
||||||
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
|
struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
|
||||||
{
|
{
|
||||||
struct burn_write_opts *opts;
|
struct burn_write_opts *opts;
|
||||||
|
|
||||||
opts = malloc(sizeof(struct burn_write_opts));
|
opts = malloc(sizeof(struct burn_write_opts));
|
||||||
|
if (opts == NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Could not allocate new auxiliary object", 0, 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
opts->drive = drive;
|
opts->drive = drive;
|
||||||
opts->refcount = 1;
|
opts->refcount = 1;
|
||||||
opts->write_type = BURN_WRITE_TAO;
|
opts->write_type = BURN_WRITE_TAO;
|
||||||
@ -19,6 +33,9 @@ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
|
|||||||
opts->simulate = 0;
|
opts->simulate = 0;
|
||||||
opts->underrun_proof = drive->mdata->underrun_proof;
|
opts->underrun_proof = drive->mdata->underrun_proof;
|
||||||
opts->perform_opc = 1;
|
opts->perform_opc = 1;
|
||||||
|
opts->obs = -1;
|
||||||
|
opts->obs_pad = 0;
|
||||||
|
opts->start_byte = -1;
|
||||||
opts->has_mediacatalog = 0;
|
opts->has_mediacatalog = 0;
|
||||||
opts->format = BURN_CDROM;
|
opts->format = BURN_CDROM;
|
||||||
opts->multi = 0;
|
opts->multi = 0;
|
||||||
@ -61,14 +78,32 @@ int burn_write_opts_set_write_type(struct burn_write_opts *opts,
|
|||||||
enum burn_write_types write_type,
|
enum burn_write_types write_type,
|
||||||
int block_type)
|
int block_type)
|
||||||
{
|
{
|
||||||
if ((write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO) ||
|
int sector_get_outmode(enum burn_write_types write_type,
|
||||||
(opts->drive->block_types[write_type] & block_type)) {
|
enum burn_block_types block_type);
|
||||||
opts->write_type = write_type;
|
int spc_block_type(enum burn_block_types b);
|
||||||
opts->block_type = block_type;
|
|
||||||
return 1;
|
/* ts A61007 */
|
||||||
|
if (! ( (write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO)
|
||||||
|
|| (opts->drive->block_types[write_type] & block_type) ) ) {
|
||||||
|
bad_combination:;
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020112,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Bad combination of write_type and block_type", 0, 0);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
assert(0);
|
/* ts A61007 : obsoleting Assert in sector.c:get_outmode() */
|
||||||
return 0;
|
if (sector_get_outmode(write_type, (enum burn_block_types) block_type)
|
||||||
|
== -1)
|
||||||
|
goto bad_combination;
|
||||||
|
/* ts A61007 : obsoleting Assert in spc.c:spc_block_type() */
|
||||||
|
if (spc_block_type((enum burn_block_types) block_type) == -1)
|
||||||
|
goto bad_combination;
|
||||||
|
|
||||||
|
opts->write_type = write_type;
|
||||||
|
opts->block_type = block_type;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* a ssert(0); */
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
|
void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
|
||||||
@ -121,6 +156,111 @@ void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts,
|
|||||||
memcpy(opts->mediacatalog, &mediacatalog, 13);
|
memcpy(opts->mediacatalog, &mediacatalog, 13);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61106 */
|
||||||
|
void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi)
|
||||||
|
{
|
||||||
|
opts->multi = !!multi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61222 */
|
||||||
|
void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value)
|
||||||
|
{
|
||||||
|
opts->start_byte = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70207 API */
|
||||||
|
enum burn_write_types burn_write_opts_auto_write_type(
|
||||||
|
struct burn_write_opts *opts, struct burn_disc *disc,
|
||||||
|
char reasons[1024], int flag)
|
||||||
|
{
|
||||||
|
struct burn_multi_caps *caps = NULL;
|
||||||
|
struct burn_drive *d = opts->drive;
|
||||||
|
struct burn_disc_mode_demands demands;
|
||||||
|
int ret;
|
||||||
|
char *reason_pt;
|
||||||
|
|
||||||
|
reasons[0] = 0;
|
||||||
|
ret = burn_disc_get_write_mode_demands(disc, &demands, 0);
|
||||||
|
if (ret <= 0) {
|
||||||
|
strcat(reasons, "cannot recognize job demands, ");
|
||||||
|
return BURN_WRITE_NONE;
|
||||||
|
}
|
||||||
|
if (demands.exotic_track && !d->current_is_cd_profile) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||||
|
0x00020123,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"DVD Media are unsuitable for desired track type",
|
||||||
|
0, 0);
|
||||||
|
if (demands.audio)
|
||||||
|
strcat(reasons, "audio track prohibited by non-CD, ");
|
||||||
|
else
|
||||||
|
strcat(reasons, "exotic track prohibited by non-CD, ");
|
||||||
|
return BURN_WRITE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = burn_disc_get_multi_caps(d, BURN_WRITE_SAO, &caps, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
no_caps:;
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||||
|
0x0002012a,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Cannot inquire write mode capabilities",
|
||||||
|
0, 0);
|
||||||
|
strcat(reasons, "cannot inquire write mode capabilities, ");
|
||||||
|
return BURN_WRITE_NONE;
|
||||||
|
} if (ret > 0) {
|
||||||
|
reason_pt = reasons + strlen(reasons);
|
||||||
|
strcat(reasons, "SAO: ");
|
||||||
|
if ((opts->multi || demands.multi_session) &&
|
||||||
|
!caps->multi_session)
|
||||||
|
strcat(reasons, "multi session capability lacking, ");
|
||||||
|
if (demands.multi_track && !caps->multi_track)
|
||||||
|
strcat(reasons, "multi track capability lacking, ");
|
||||||
|
if (demands.unknown_track_size)
|
||||||
|
strcat(reasons, "track size unpredictable, ");
|
||||||
|
if (demands.mixed_mode)
|
||||||
|
strcat(reasons, "tracks of different modes mixed, ");
|
||||||
|
if (strcmp(reason_pt, "SAO: ") != 0)
|
||||||
|
goto no_sao;
|
||||||
|
burn_write_opts_set_write_type(opts,
|
||||||
|
BURN_WRITE_SAO, BURN_BLOCK_SAO);
|
||||||
|
return BURN_WRITE_SAO;
|
||||||
|
} else
|
||||||
|
strcat(reasons, "SAO: no SAO offered by drive and media, ");
|
||||||
|
no_sao:;
|
||||||
|
burn_disc_free_multi_caps(&caps);
|
||||||
|
strcat(reasons, "\n");
|
||||||
|
reason_pt = reasons + strlen(reasons);
|
||||||
|
strcat(reasons, "TAO: ");
|
||||||
|
ret = burn_disc_get_multi_caps(d, BURN_WRITE_TAO, &caps, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto no_caps;
|
||||||
|
if (ret == 0) {
|
||||||
|
strcat(reasons, "no TAO offered by drive and media, ");
|
||||||
|
no_write_mode:;
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||||
|
0x0002012b,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Drive offers no suitable write mode with this job",
|
||||||
|
0, 0);
|
||||||
|
return BURN_WRITE_NONE;
|
||||||
|
}
|
||||||
|
if ((opts->multi || demands.multi_session) && !caps->multi_session)
|
||||||
|
strcat(reasons, "multi session capability lacking, ");
|
||||||
|
if (demands.multi_track && !caps->multi_track)
|
||||||
|
strcat(reasons, "multi track capability lacking, ");
|
||||||
|
if (strcmp(reason_pt, "TAO: ") != 0)
|
||||||
|
goto no_write_mode;
|
||||||
|
/* ( TAO data/audio block size will be handled automatically ) */
|
||||||
|
burn_write_opts_set_write_type(opts,
|
||||||
|
BURN_WRITE_TAO, BURN_BLOCK_MODE1);
|
||||||
|
return BURN_WRITE_TAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
|
void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
|
||||||
{
|
{
|
||||||
opts->raw = raw;
|
opts->raw = raw;
|
||||||
@ -167,3 +307,4 @@ void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
|
|||||||
{
|
{
|
||||||
opts->hardware_error_retries = hardware_error_retries;
|
opts->hardware_error_retries = hardware_error_retries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,15 @@ struct burn_write_opts
|
|||||||
/** Perform calibration of the drive's laser before beginning the
|
/** Perform calibration of the drive's laser before beginning the
|
||||||
write. */
|
write. */
|
||||||
unsigned int perform_opc:1;
|
unsigned int perform_opc:1;
|
||||||
|
|
||||||
|
/* ts A61219 : Output block size to trigger buffer flush if hit.
|
||||||
|
-1 with CD, 32 kB with DVD */
|
||||||
|
int obs;
|
||||||
|
int obs_pad; /* 1=pad up last block to obs */
|
||||||
|
|
||||||
|
/* ts A61222 : Start address for media which allow a choice */
|
||||||
|
off_t start_byte;
|
||||||
|
|
||||||
/** A disc can have a media catalog number */
|
/** A disc can have a media catalog number */
|
||||||
int has_mediacatalog;
|
int has_mediacatalog;
|
||||||
unsigned char mediacatalog[13];
|
unsigned char mediacatalog[13];
|
||||||
|
61
libburn/os-freebsd.h
Normal file
61
libburn/os-freebsd.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
/* os-freebsd.h
|
||||||
|
Operating system specific libburn definitions and declarations. Included
|
||||||
|
by os.h in case of compilation for
|
||||||
|
FreeBSD with CAM
|
||||||
|
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** List of all signals which shall be caught by signal handlers and trigger
|
||||||
|
a graceful abort of libburn. (See man 7 signal.)
|
||||||
|
*/
|
||||||
|
/* Once as system defined macros */
|
||||||
|
#define BURN_OS_SIGNAL_MACRO_LIST \
|
||||||
|
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
|
||||||
|
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
|
||||||
|
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
|
||||||
|
SIGTTOU, \
|
||||||
|
SIGBUS, SIGPROF, SIGSYS, SIGTRAP, \
|
||||||
|
SIGVTALRM, SIGXCPU, SIGXFSZ
|
||||||
|
|
||||||
|
/* Once as text 1:1 list of strings for messages and interpreters */
|
||||||
|
#define BURN_OS_SIGNAL_NAME_LIST \
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
|
||||||
|
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
|
||||||
|
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
|
||||||
|
"SIGTTOU", \
|
||||||
|
"SIGBUS", "SIGPROF", "SIGSYS", "SIGTRAP", \
|
||||||
|
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
|
||||||
|
|
||||||
|
/* The number of above list items */
|
||||||
|
#define BURN_OS_SIGNAL_COUNT 23
|
||||||
|
|
||||||
|
/** To list all signals which shall surely not be caught */
|
||||||
|
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
|
||||||
|
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH
|
||||||
|
|
||||||
|
/* The number of above list items */
|
||||||
|
#define BURN_OS_NON_SIGNAL_COUNT 5
|
||||||
|
|
||||||
|
|
||||||
|
/* The maximum size for a (SCSI) i/o transaction */
|
||||||
|
/* Important : MUST be at least 32768 ! */
|
||||||
|
#define BURN_OS_TRANSPORT_BUFFER_SIZE 32768
|
||||||
|
|
||||||
|
|
||||||
|
/** To hold all state information of BSD device enumeration
|
||||||
|
which are now local in sg_enumerate() . So that sg_give_next_adr()
|
||||||
|
can work in BSD and sg_enumerate() can use it.
|
||||||
|
*/
|
||||||
|
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
|
||||||
|
struct burn_drive_enumeration_state; \
|
||||||
|
typedef struct burn_drive_enumeration_state *burn_drive_enumerator_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* The list of operating system dependent elements in struct burn_drive.
|
||||||
|
To be initialized and used within sg-*.c .
|
||||||
|
*/
|
||||||
|
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
|
||||||
|
struct cam_device* cam;
|
||||||
|
|
64
libburn/os-linux.h
Normal file
64
libburn/os-linux.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
/* os-linux.h
|
||||||
|
Operating system specific libburn definitions and declarations. Included
|
||||||
|
by os.h in case of compilation for
|
||||||
|
Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg)
|
||||||
|
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** List of all signals which shall be caught by signal handlers and trigger
|
||||||
|
a graceful abort of libburn. (See man 7 signal.)
|
||||||
|
*/
|
||||||
|
/* Once as system defined macros */
|
||||||
|
#define BURN_OS_SIGNAL_MACRO_LIST \
|
||||||
|
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, \
|
||||||
|
SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, \
|
||||||
|
SIGUSR1, SIGUSR2, SIGXCPU, SIGTSTP, SIGTTIN, \
|
||||||
|
SIGTTOU, \
|
||||||
|
SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, \
|
||||||
|
SIGVTALRM, SIGXCPU, SIGXFSZ
|
||||||
|
|
||||||
|
/* Once as text 1:1 list of strings for messages and interpreters */
|
||||||
|
#define BURN_OS_SIGNAL_NAME_LIST \
|
||||||
|
"SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGABRT", \
|
||||||
|
"SIGFPE", "SIGSEGV", "SIGPIPE", "SIGALRM", "SIGTERM", \
|
||||||
|
"SIGUSR1", "SIGUSR2", "SIGXCPU", "SIGTSTP", "SIGTTIN", \
|
||||||
|
"SIGTTOU", \
|
||||||
|
"SIGBUS", "SIGPOLL", "SIGPROF", "SIGSYS", "SIGTRAP", \
|
||||||
|
"SIGVTALRM", "SIGXCPU", "SIGXFSZ"
|
||||||
|
|
||||||
|
/* The number of above list items */
|
||||||
|
#define BURN_OS_SIGNAL_COUNT 24
|
||||||
|
|
||||||
|
/** To list all signals which shall surely not be caught */
|
||||||
|
#define BURN_OS_NON_SIGNAL_MACRO_LIST \
|
||||||
|
SIGKILL, SIGCHLD, SIGSTOP, SIGURG, SIGWINCH
|
||||||
|
|
||||||
|
/* The number of above list items */
|
||||||
|
#define BURN_OS_NON_SIGNAL_COUNT 5
|
||||||
|
|
||||||
|
|
||||||
|
/* The maximum size for a (SCSI) i/o transaction */
|
||||||
|
/* Important : MUST be at least 32768 ! */
|
||||||
|
#define BURN_OS_TRANSPORT_BUFFER_SIZE 65536
|
||||||
|
|
||||||
|
|
||||||
|
/* To hold the index number of the most recently delivered address from
|
||||||
|
device enumeration.
|
||||||
|
*/
|
||||||
|
#define BURN_OS_DEFINE_DRIVE_ENUMERATOR_T \
|
||||||
|
typedef int burn_drive_enumerator_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* The list of operating system dependent elements in struct burn_drive.
|
||||||
|
Usually they are initialized in sg-*.c:enumerate_common().
|
||||||
|
*/
|
||||||
|
#define BURN_OS_TRANSPORT_DRIVE_ELEMENTS \
|
||||||
|
int fd; \
|
||||||
|
\
|
||||||
|
/* ts A60926 : trying to lock against growisofs /dev/srN, /dev/scdN */ \
|
||||||
|
int sibling_count; \
|
||||||
|
int sibling_fds[LIBBURN_SG_MAX_SIBLINGS];
|
||||||
|
|
34
libburn/os.h
Normal file
34
libburn/os.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
/* os.h
|
||||||
|
Operating system specific libburn definitions and declarations.
|
||||||
|
The macros defined here are used by libburn modules in order to
|
||||||
|
avoid own system dependent case distinctions.
|
||||||
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BURN_OS_H_INCLUDED
|
||||||
|
#define BURN_OS_H_INCLUDED 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
Operating system case distinction
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------- FreeBSD with CAM -------------------------- */
|
||||||
|
#include "os-freebsd.h"
|
||||||
|
|
||||||
|
|
||||||
|
#else /* operating system case distinction */
|
||||||
|
|
||||||
|
|
||||||
|
/* --------- Linux kernels 2.4 and 2.6 with Linux SCSI Generic (sg) -------- */
|
||||||
|
#include "os-linux.h"
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* End of operating system case distinction */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ! BURN_OS_H_INCLUDED */
|
||||||
|
|
@ -1,9 +1,14 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <malloc.h>
|
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <assert.h>
|
|
||||||
|
/* ts A61007 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -11,7 +16,11 @@
|
|||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
#include "drive.h"
|
#include "drive.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
|
/* ts A60925 : obsoleted by libdax_msgs.h
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
*/
|
||||||
|
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
@ -33,11 +42,16 @@ void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o)
|
|||||||
struct buffer page;
|
struct buffer page;
|
||||||
int speed;
|
int speed;
|
||||||
|
|
||||||
assert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
/* ts A61007 : if this function gets revived, then these
|
||||||
|
tests have to be done more graceful */
|
||||||
|
a ssert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000));
|
||||||
|
a ssert(!d->busy);
|
||||||
|
a ssert(d->toc->valid);
|
||||||
|
a ssert(o->datafd != -1);
|
||||||
|
|
||||||
|
/* moved up from spc_select_error_params alias d->send_parameters() */
|
||||||
|
a ssert(d->mdata->valid);
|
||||||
|
|
||||||
assert(!d->busy);
|
|
||||||
assert(d->toc->valid);
|
|
||||||
assert(o->datafd != -1);
|
|
||||||
/* XXX not sure this is a good idea. copy it? */
|
/* XXX not sure this is a good idea. copy it? */
|
||||||
/* XXX also, we have duplicated data now, do we remove the fds from struct
|
/* XXX also, we have duplicated data now, do we remove the fds from struct
|
||||||
drive, or only store a subset of the _opts structs in drives */
|
drive, or only store a subset of the _opts structs in drives */
|
||||||
@ -47,6 +61,7 @@ drive, or only store a subset of the _opts structs in drives */
|
|||||||
d->set_speed(d, speed, 0);
|
d->set_speed(d, speed, 0);
|
||||||
|
|
||||||
d->params.retries = o->hardware_error_retries;
|
d->params.retries = o->hardware_error_retries;
|
||||||
|
|
||||||
d->send_parameters(d, o);
|
d->send_parameters(d, o);
|
||||||
|
|
||||||
d->cancel = 0;
|
d->cancel = 0;
|
||||||
@ -124,7 +139,10 @@ drive, or only store a subset of the _opts structs in drives */
|
|||||||
page.sectors = (finish < maxsects) ? finish : maxsects;
|
page.sectors = (finish < maxsects) ? finish : maxsects;
|
||||||
printf("reading %d sectors from %d\n", page.sectors,
|
printf("reading %d sectors from %d\n", page.sectors,
|
||||||
drive_lba);
|
drive_lba);
|
||||||
d->read_sectors(d, drive_lba, page.sectors, o, &page);
|
|
||||||
|
/* >>> ts A61009 : ensure page.sectors >= 0 before calling */
|
||||||
|
d->r ead_sectors(d, drive_lba, page.sectors, o, &page);
|
||||||
|
|
||||||
printf("Read %d\n", page.sectors);
|
printf("Read %d\n", page.sectors);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
/* scsi block commands */
|
/* scsi block commands */
|
||||||
|
|
||||||
#include <scsi/scsi.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <scsi/sg.h>
|
|
||||||
|
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
#include "sbc.h"
|
#include "sbc.h"
|
||||||
@ -13,6 +11,7 @@
|
|||||||
/* spc command set */
|
/* spc command set */
|
||||||
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
static char SBC_LOAD[] = { 0x1b, 0, 0, 0, 3, 0 };
|
||||||
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
|
static char SBC_UNLOAD[] = { 0x1b, 0, 0, 0, 2, 0 };
|
||||||
|
static char SBC_START_UNIT[] = { 0x1b, 0, 0, 0, 1, 0 };
|
||||||
|
|
||||||
void sbc_load(struct burn_drive *d)
|
void sbc_load(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
@ -38,3 +37,29 @@ void sbc_eject(struct burn_drive *d)
|
|||||||
c.dir = NO_TRANSFER;
|
c.dir = NO_TRANSFER;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ts A61118 : is it necessary to tell the drive to get ready for use ? */
|
||||||
|
int sbc_start_unit(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
struct command c;
|
||||||
|
|
||||||
|
memcpy(c.opcode, SBC_START_UNIT, sizeof(SBC_START_UNIT));
|
||||||
|
c.retry = 1;
|
||||||
|
c.oplen = sizeof(SBC_START_UNIT);
|
||||||
|
c.dir = NO_TRANSFER;
|
||||||
|
c.page = NULL;
|
||||||
|
d->issue_command(d, &c);
|
||||||
|
return (c.error==0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int sbc_setup_drive(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
d->eject = sbc_eject;
|
||||||
|
d->load = sbc_load;
|
||||||
|
d->start_unit = sbc_start_unit;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,11 @@ struct burn_drive;
|
|||||||
void sbc_load(struct burn_drive *);
|
void sbc_load(struct burn_drive *);
|
||||||
void sbc_eject(struct burn_drive *);
|
void sbc_eject(struct burn_drive *);
|
||||||
|
|
||||||
|
/* ts A61118 */
|
||||||
|
int sbc_start_unit(struct burn_drive *);
|
||||||
|
|
||||||
|
/* ts A61021 : the sbc specific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int sbc_setup_drive(struct burn_drive *d);
|
||||||
|
|
||||||
#endif /* __SBC */
|
#endif /* __SBC */
|
||||||
|
271
libburn/sector.c
271
libburn/sector.c
@ -1,7 +1,10 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
|
||||||
|
/* ts A61010 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@ -16,6 +19,15 @@
|
|||||||
#include "toc.h"
|
#include "toc.h"
|
||||||
#include "write.h"
|
#include "write.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
|
/* <<< ts A61031 */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif /* Libburn_log_in_and_out_streaM */
|
||||||
|
|
||||||
|
|
||||||
/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/
|
/*static unsigned char isrc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";*/
|
||||||
|
|
||||||
#define sector_common(X) d->alba++; d->rlba X;
|
#define sector_common(X) d->alba++; d->rlba X;
|
||||||
@ -37,13 +49,15 @@ static void uncook_subs(unsigned char *dest, unsigned char *source)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 0 means "same as inmode" */
|
/* @return >=0 : valid , <0 invalid */
|
||||||
static int get_outmode(struct burn_write_opts *o)
|
int sector_get_outmode(enum burn_write_types write_type,
|
||||||
|
enum burn_block_types block_type)
|
||||||
{
|
{
|
||||||
if (o->write_type == BURN_WRITE_SAO)
|
/* ts A61103 : extended SAO condition to TAO */
|
||||||
|
if (write_type == BURN_WRITE_SAO || write_type == BURN_WRITE_TAO)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
switch (o->block_type) {
|
switch (block_type) {
|
||||||
case BURN_BLOCK_RAW0:
|
case BURN_BLOCK_RAW0:
|
||||||
return BURN_MODE_RAW;
|
return BURN_MODE_RAW;
|
||||||
case BURN_BLOCK_RAW16:
|
case BURN_BLOCK_RAW16:
|
||||||
@ -54,13 +68,37 @@ static int get_outmode(struct burn_write_opts *o)
|
|||||||
return BURN_MODE_RAW | BURN_SUBCODE_R96;
|
return BURN_MODE_RAW | BURN_SUBCODE_R96;
|
||||||
case BURN_BLOCK_MODE1:
|
case BURN_BLOCK_MODE1:
|
||||||
return BURN_MODE1;
|
return BURN_MODE1;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
assert(0); /* return BURN_MODE_UNIMPLEMENTED :) */
|
|
||||||
|
/* ts A61007 : now handled in burn_write_opts_set_write_type() */
|
||||||
|
/* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 0 means "same as inmode" */
|
||||||
|
static int get_outmode(struct burn_write_opts *o)
|
||||||
|
{
|
||||||
|
/* ts A61007 */
|
||||||
|
return sector_get_outmode(o->write_type, o->block_type);
|
||||||
|
|
||||||
|
/* -1 is prevented by check in burn_write_opts_set_write_type() */
|
||||||
|
/* a ssert(0); */ /* return BURN_MODE_UNIMPLEMENTED :) */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
||||||
{
|
{
|
||||||
int valid, shortage, curr;
|
int valid, shortage, curr, i, tr;
|
||||||
|
|
||||||
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
|
/* <<< ts A61031 */
|
||||||
|
static int tee_fd= -1;
|
||||||
|
if(tee_fd==-1)
|
||||||
|
tee_fd= open("/tmp/libburn_sg_readin",
|
||||||
|
O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
|
||||||
|
#endif /* Libburn_log_in_and_out_streaM */
|
||||||
|
|
||||||
|
|
||||||
/* no track pointer means we're just generating 0s */
|
/* no track pointer means we're just generating 0s */
|
||||||
if (!track) {
|
if (!track) {
|
||||||
@ -80,7 +118,7 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
shortage = count - valid;
|
shortage = count - valid;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
return;
|
goto ex;
|
||||||
|
|
||||||
/* Next we use source data */
|
/* Next we use source data */
|
||||||
curr = valid;
|
curr = valid;
|
||||||
@ -88,16 +126,24 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
valid = track->source->read(track->source, data + curr, count - curr);
|
valid = track->source->read(track->source, data + curr, count - curr);
|
||||||
} else valid = 0;
|
} else valid = 0;
|
||||||
|
|
||||||
if (valid == -1) {
|
if (valid <= 0) { /* ts A61031 : extended from (valid == -1) */
|
||||||
track->eos = 1;
|
track->eos = 1;
|
||||||
valid = 0;
|
valid = 0;
|
||||||
}
|
}
|
||||||
|
track->sourcecount += valid;
|
||||||
|
|
||||||
|
#ifdef Libburn_log_in_and_out_streaM
|
||||||
|
/* <<< ts A61031 */
|
||||||
|
if(tee_fd!=-1 && valid>0) {
|
||||||
|
write(tee_fd, data + curr, valid);
|
||||||
|
}
|
||||||
|
#endif /* Libburn_log_in_and_out_streaM */
|
||||||
|
|
||||||
curr += valid;
|
curr += valid;
|
||||||
shortage = count - curr;
|
shortage = count - curr;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
return;
|
goto ex;
|
||||||
|
|
||||||
/* Before going to the next track, we run through any tail */
|
/* Before going to the next track, we run through any tail */
|
||||||
|
|
||||||
@ -113,7 +159,13 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
shortage -= valid;
|
shortage -= valid;
|
||||||
|
|
||||||
if (!shortage)
|
if (!shortage)
|
||||||
return;
|
goto ex;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
if (shortage >= count)
|
||||||
|
track->track_data_done = 1;
|
||||||
|
if (track->open_ended)
|
||||||
|
goto ex;
|
||||||
|
|
||||||
/* If we're still short, and there's a "next" pointer, we pull from that.
|
/* If we're still short, and there's a "next" pointer, we pull from that.
|
||||||
if that depletes, we'll just fill with 0s.
|
if that depletes, we'll just fill with 0s.
|
||||||
@ -128,11 +180,24 @@ static void get_bytes(struct burn_track *track, int count, unsigned char *data)
|
|||||||
curr += valid;
|
curr += valid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!shortage)
|
ex:;
|
||||||
return;
|
/* ts A61024 : general finalizing processing */
|
||||||
memset(data + curr, 0, shortage);
|
if(shortage)
|
||||||
|
memset(data + curr, 0, shortage); /* this is old icculus.org */
|
||||||
|
if (track->swap_source_bytes == 1) {
|
||||||
|
for (i = 1; i < count; i += 2) {
|
||||||
|
tr = data[i];
|
||||||
|
data[i] = data[i-1];
|
||||||
|
data[i-1] = tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
|
||||||
|
/* ts A61009 : seems to hand out sector start pointer in opts->drive->buffer
|
||||||
|
and to count hand outs as well as reserved bytes */
|
||||||
|
/* ts A61101 : added parameter track for counting written bytes */
|
||||||
|
static unsigned char *get_sector(struct burn_write_opts *opts,
|
||||||
|
struct burn_track *track, int inmode)
|
||||||
{
|
{
|
||||||
struct burn_drive *d = opts->drive;
|
struct burn_drive *d = opts->drive;
|
||||||
struct buffer *out = d->buffer;
|
struct buffer *out = d->buffer;
|
||||||
@ -144,13 +209,29 @@ static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
|||||||
if (outmode == 0)
|
if (outmode == 0)
|
||||||
outmode = inmode;
|
outmode = inmode;
|
||||||
|
|
||||||
seclen = burn_sector_length(outmode) + burn_subcode_length(outmode);
|
/* ts A61009 : react on eventual failure of burn_sector_length()
|
||||||
|
(should not happen if API tested properly).
|
||||||
|
Ensures out->bytes >= out->sectors */
|
||||||
|
seclen = burn_sector_length(outmode);
|
||||||
|
if (seclen <= 0)
|
||||||
|
return NULL;
|
||||||
|
seclen += burn_subcode_length(outmode);
|
||||||
|
|
||||||
if (out->bytes + (seclen) >= BUFFER_SIZE) {
|
/* ts A61219 : opts->obs is eventually a 32k trigger for DVD */
|
||||||
|
if (out->bytes + (seclen) > BUFFER_SIZE || out->bytes == opts->obs) {
|
||||||
int err;
|
int err;
|
||||||
err = d->write(d, d->nwa, out);
|
err = d->write(d, d->nwa, out);
|
||||||
if (err == BE_CANCELLED)
|
if (err == BE_CANCELLED)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* ts A61101 */
|
||||||
|
if(track != NULL) {
|
||||||
|
track->writecount += out->bytes;
|
||||||
|
track->written_sectors += out->sectors;
|
||||||
|
}
|
||||||
|
/* ts A61119 */
|
||||||
|
d->progress.buffered_bytes += out->bytes;
|
||||||
|
|
||||||
d->nwa += out->sectors;
|
d->nwa += out->sectors;
|
||||||
out->bytes = 0;
|
out->bytes = 0;
|
||||||
out->sectors = 0;
|
out->sectors = 0;
|
||||||
@ -163,8 +244,36 @@ static unsigned char *get_sector(struct burn_write_opts *opts, int inmode)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
/* Revoke the counting of the most recent sector handed out by get_sector() */
|
||||||
|
static void unget_sector(struct burn_write_opts *opts, int inmode)
|
||||||
|
{
|
||||||
|
struct burn_drive *d = opts->drive;
|
||||||
|
struct buffer *out = d->buffer;
|
||||||
|
int outmode;
|
||||||
|
int seclen;
|
||||||
|
|
||||||
|
outmode = get_outmode(opts);
|
||||||
|
if (outmode == 0)
|
||||||
|
outmode = inmode;
|
||||||
|
|
||||||
|
/* ts A61009 : react on eventual failure of burn_sector_length()
|
||||||
|
(should not happen if API tested properly).
|
||||||
|
Ensures out->bytes >= out->sectors */
|
||||||
|
seclen = burn_sector_length(outmode);
|
||||||
|
if (seclen <= 0)
|
||||||
|
return;
|
||||||
|
seclen += burn_subcode_length(outmode);
|
||||||
|
|
||||||
|
out->bytes -= seclen;
|
||||||
|
out->sectors--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* either inmode == outmode, or outmode == raw. anything else is bad news */
|
/* either inmode == outmode, or outmode == raw. anything else is bad news */
|
||||||
static void convert_data(struct burn_write_opts *o, struct burn_track *track,
|
/* ts A61010 : changed type to int in order to propagate said bad news */
|
||||||
|
/** @return 1 is ok, <= 0 is failure */
|
||||||
|
static int convert_data(struct burn_write_opts *o, struct burn_track *track,
|
||||||
int inmode, unsigned char *data)
|
int inmode, unsigned char *data)
|
||||||
{
|
{
|
||||||
int outlen, inlen;
|
int outlen, inlen;
|
||||||
@ -177,14 +286,21 @@ static void convert_data(struct burn_write_opts *o, struct burn_track *track,
|
|||||||
|
|
||||||
outlen = burn_sector_length(outmode);
|
outlen = burn_sector_length(outmode);
|
||||||
inlen = burn_sector_length(inmode);
|
inlen = burn_sector_length(inmode);
|
||||||
assert(outlen >= inlen);
|
|
||||||
|
/* ts A61010 */
|
||||||
|
/* a ssert(outlen >= inlen); */
|
||||||
|
if (outlen < inlen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
if ((outmode & BURN_MODE_BITS) == (inmode & BURN_MODE_BITS)) {
|
||||||
get_bytes(track, inlen, data);
|
get_bytes(track, inlen, data);
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(outmode & BURN_MODE_RAW);
|
/* ts A61010 */
|
||||||
|
/* a ssert(outmode & BURN_MODE_RAW); */
|
||||||
|
if (!(outmode & BURN_MODE_RAW))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (inmode & BURN_MODE1)
|
if (inmode & BURN_MODE1)
|
||||||
offset = 16;
|
offset = 16;
|
||||||
@ -192,9 +308,16 @@ static void convert_data(struct burn_write_opts *o, struct burn_track *track,
|
|||||||
offset = 0;
|
offset = 0;
|
||||||
if (inmode & BURN_AUDIO)
|
if (inmode & BURN_AUDIO)
|
||||||
offset = 0;
|
offset = 0;
|
||||||
assert(offset != -1);
|
|
||||||
|
/* ts A61010 */
|
||||||
|
/* a ssert(offset != -1); */
|
||||||
|
if (offset == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
get_bytes(track, inlen, data + offset);
|
get_bytes(track, inlen, data + offset);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_subs(struct burn_write_opts *o, int inmode,
|
static void convert_subs(struct burn_write_opts *o, int inmode,
|
||||||
unsigned char *subs, unsigned char *sector)
|
unsigned char *subs, unsigned char *sector)
|
||||||
{
|
{
|
||||||
@ -228,6 +351,8 @@ static void convert_subs(struct burn_write_opts *o, int inmode,
|
|||||||
out[0] = ~out[0];
|
out[0] = ~out[0];
|
||||||
out[1] = ~out[1];
|
out[1] = ~out[1];
|
||||||
break;
|
break;
|
||||||
|
/* ts A61119 : to silence compiler warnings */
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,10 +399,12 @@ int sector_toc(struct burn_write_opts *o, int mode)
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
|
|
||||||
data = get_sector(o, mode);
|
data = get_sector(o, NULL, mode);
|
||||||
if (!data)
|
if (data == NULL)
|
||||||
|
return 0;
|
||||||
|
/* ts A61010 */
|
||||||
|
if (convert_data(o, NULL, mode, data) <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
convert_data(o, NULL, mode, data);
|
|
||||||
subcode_toc(d, mode, subs);
|
subcode_toc(d, mode, subs);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 1);
|
sector_headers(o, data, mode, 1);
|
||||||
@ -292,10 +419,12 @@ int sector_pregap(struct burn_write_opts *o,
|
|||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
|
|
||||||
data = get_sector(o, mode);
|
data = get_sector(o, NULL, mode);
|
||||||
if (!data)
|
if (data == NULL)
|
||||||
|
return 0;
|
||||||
|
/* ts A61010 */
|
||||||
|
if (convert_data(o, NULL, mode, data) <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
convert_data(o, NULL, mode, data);
|
|
||||||
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
subcode_user(o, subs, tno, control, 0, NULL, 1);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 0);
|
sector_headers(o, data, mode, 0);
|
||||||
@ -310,10 +439,12 @@ int sector_postgap(struct burn_write_opts *o,
|
|||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, mode);
|
data = get_sector(o, NULL, mode);
|
||||||
if (!data)
|
if (data == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
convert_data(o, NULL, mode, data);
|
/* ts A61010 */
|
||||||
|
if (convert_data(o, NULL, mode, data) <= 0)
|
||||||
|
return 0;;
|
||||||
/* use last index in track */
|
/* use last index in track */
|
||||||
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
subcode_user(o, subs, tno, control, 1, NULL, 1);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
@ -362,7 +493,9 @@ static char char_to_isrc(char c)
|
|||||||
return 0x11 + (c - 'A');
|
return 0x11 + (c - 'A');
|
||||||
if (c >= 'a' && c <= 'z')
|
if (c >= 'a' && c <= 'z')
|
||||||
return 0x11 + (c - 'a');
|
return 0x11 + (c - 'a');
|
||||||
assert(0);
|
|
||||||
|
/* ts A61008 : obsoleted by test in burn_track_set_isrc() */
|
||||||
|
/* a ssert(0); */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +531,8 @@ void subcode_user(struct burn_write_opts *o, unsigned char *subcodes,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(qmode == 1 || qmode == 2 || qmode == 3);
|
/* ts A61010 : this cannot happen. Assert for fun ? */
|
||||||
|
/* a ssert(qmode == 1 || qmode == 2 || qmode == 3); */
|
||||||
|
|
||||||
switch (qmode) {
|
switch (qmode) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -477,10 +611,12 @@ int sector_lout(struct burn_write_opts *o, unsigned char control, int mode)
|
|||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, mode);
|
data = get_sector(o, NULL, mode);
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
convert_data(o, NULL, mode, data);
|
/* ts A61010 */
|
||||||
|
if (convert_data(o, NULL, mode, data) <= 0)
|
||||||
|
return 0;
|
||||||
subcode_lout(o, control, subs);
|
subcode_lout(o, control, subs);
|
||||||
convert_subs(o, mode, subs, data);
|
convert_subs(o, mode, subs, data);
|
||||||
sector_headers(o, data, mode, 0);
|
sector_headers(o, data, mode, 0);
|
||||||
@ -494,12 +630,23 @@ int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub)
|
|||||||
unsigned char subs[96];
|
unsigned char subs[96];
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
data = get_sector(o, t->mode);
|
data = get_sector(o, t, t->mode);
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
convert_data(o, t, t->mode, data);
|
/* ts A61010 */
|
||||||
|
if (convert_data(o, t, t->mode, data) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!t->source->read_sub)
|
/* ts A61031 */
|
||||||
|
if (t->open_ended && t->track_data_done) {
|
||||||
|
unget_sector(o, t->mode);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A61219 : allow track without .entry */
|
||||||
|
if (t->entry == NULL)
|
||||||
|
;
|
||||||
|
else if (!t->source->read_sub)
|
||||||
subcode_user(o, subs, t->entry->point,
|
subcode_user(o, subs, t->entry->point,
|
||||||
t->entry->control, 1, &t->isrc, psub);
|
t->entry->control, 1, &t->isrc, psub);
|
||||||
else if (!t->source->read_sub(t->source, subs, 96))
|
else if (!t->source->read_sub(t->source, subs, 96))
|
||||||
@ -542,6 +689,22 @@ int dec_to_bcd(int d)
|
|||||||
return (top << 4) + bottom;
|
return (top << 4) + bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sector_headers_is_ok(struct burn_write_opts *o, int mode)
|
||||||
|
{
|
||||||
|
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||||
|
return 1;
|
||||||
|
if (o->write_type == BURN_WRITE_SAO)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
if (o->write_type == BURN_WRITE_TAO)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (mode & BURN_MODE1)
|
||||||
|
return 2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
||||||
int mode, int leadin)
|
int mode, int leadin)
|
||||||
{
|
{
|
||||||
@ -550,14 +713,33 @@ void sector_headers(struct burn_write_opts *o, unsigned char *out,
|
|||||||
int min, sec, frame;
|
int min, sec, frame;
|
||||||
int modebyte = -1;
|
int modebyte = -1;
|
||||||
|
|
||||||
|
/* ts A61009 */
|
||||||
|
#if 1
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sector_headers_is_ok(o, mode);
|
||||||
|
if (ret != 2)
|
||||||
|
return;
|
||||||
|
modebyte = 1;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
if (mode & BURN_AUDIO) /* no headers for "audio" */
|
||||||
return;
|
return;
|
||||||
if (o->write_type == BURN_WRITE_SAO)
|
if (o->write_type == BURN_WRITE_SAO)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
if (o->write_type == BURN_WRITE_TAO)
|
||||||
|
return;
|
||||||
|
|
||||||
if (mode & BURN_MODE1)
|
if (mode & BURN_MODE1)
|
||||||
modebyte = 1;
|
modebyte = 1;
|
||||||
|
|
||||||
assert(modebyte == 1);
|
#endif
|
||||||
|
|
||||||
|
/* ts A61009 : now ensured by burn_disc_write_is_ok() */
|
||||||
|
/* a ssert(modebyte == 1); */
|
||||||
|
|
||||||
out[0] = 0;
|
out[0] = 0;
|
||||||
memset(out + 1, 0xFF, 10); /* sync */
|
memset(out + 1, 0xFF, 10); /* sync */
|
||||||
@ -629,13 +811,22 @@ void process_q(struct burn_drive *d, unsigned char *q)
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
|
||||||
|
/* ts A61009 : if reactivated then witout Assert */
|
||||||
|
a ssert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* this needs more info. subs in the data? control/adr? */
|
/* this needs more info. subs in the data? control/adr? */
|
||||||
|
|
||||||
|
/* ts A61119 : One should not use inofficial compiler extensions.
|
||||||
|
>>> Some day this function needs to be implemented. At least for now
|
||||||
|
the result does not match the "mode" of cdrecord -toc.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
#warning sector_identify needs to be written
|
#warning sector_identify needs to be written
|
||||||
|
*/
|
||||||
int sector_identify(unsigned char *data)
|
int sector_identify(unsigned char *data)
|
||||||
{
|
{
|
||||||
scramble(data);
|
scramble(data);
|
||||||
|
@ -18,6 +18,10 @@ int sector_postgap(struct burn_write_opts *, unsigned char tno,
|
|||||||
unsigned char control, int mode);
|
unsigned char control, int mode);
|
||||||
int sector_lout(struct burn_write_opts *, unsigned char control, int mode);
|
int sector_lout(struct burn_write_opts *, unsigned char control, int mode);
|
||||||
int sector_data(struct burn_write_opts *, struct burn_track *t, int psub);
|
int sector_data(struct burn_write_opts *, struct burn_track *t, int psub);
|
||||||
|
|
||||||
|
/* ts A61009 */
|
||||||
|
int sector_headers_is_ok(struct burn_write_opts *o, int mode);
|
||||||
|
|
||||||
void sector_headers(struct burn_write_opts *, unsigned char *,
|
void sector_headers(struct burn_write_opts *, unsigned char *,
|
||||||
int mode, int leadin);
|
int mode, int leadin);
|
||||||
void subcode_user(struct burn_write_opts *, unsigned char *s,
|
void subcode_user(struct burn_write_opts *, unsigned char *s,
|
||||||
|
559
libburn/sg-freebsd-port.c
Normal file
559
libburn/sg-freebsd-port.c
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This is the main operating system dependent SCSI part of libburn. It implements
|
||||||
|
the transport level aspects of SCSI control and command i/o.
|
||||||
|
|
||||||
|
Present implementation: FreeBSD CAM (untested)
|
||||||
|
|
||||||
|
|
||||||
|
PORTING:
|
||||||
|
|
||||||
|
Porting libburn typically will consist of adding a new operating system case
|
||||||
|
to the following switcher files:
|
||||||
|
os.h Operating system specific libburn definitions and declarations.
|
||||||
|
sg.c Operating system dependent transport level modules.
|
||||||
|
and of deriving the following system specific files from existing examples:
|
||||||
|
os-*.h Included by os.h. You will need some general system knowledge
|
||||||
|
about signals and knowledge about the storage object needs of your
|
||||||
|
transport level module sg-*.c.
|
||||||
|
|
||||||
|
sg-*.c This source module. You will need special system knowledge about
|
||||||
|
how to detect all potentially available drives, how to open them,
|
||||||
|
eventually how to exclusively reserve them, how to perform
|
||||||
|
SCSI transactions, how to inquire the (pseudo-)SCSI driver.
|
||||||
|
You will not need to care about CD burning, MMC or other high-level
|
||||||
|
SCSI aspects.
|
||||||
|
|
||||||
|
Said sg-*.c operations are defined by a public function interface, which has
|
||||||
|
to be implemented in a way that provides libburn with the desired services:
|
||||||
|
|
||||||
|
sg_give_next_adr() iterates over the set of potentially useful drive
|
||||||
|
address strings.
|
||||||
|
|
||||||
|
scsi_enumerate_drives() brings all available, not-whitelist-banned, and
|
||||||
|
accessible drives into libburn's list of drives.
|
||||||
|
|
||||||
|
sg_drive_is_open() tells wether libburn has the given drive in use.
|
||||||
|
|
||||||
|
sg_grab() opens the drive for SCSI commands and ensures
|
||||||
|
undisturbed access.
|
||||||
|
|
||||||
|
sg_release() closes a drive opened by sg_grab()
|
||||||
|
|
||||||
|
sg_issue_command() sends a SCSI command to the drive, receives reply,
|
||||||
|
and evaluates wether the command succeeded or shall
|
||||||
|
be retried or finally failed.
|
||||||
|
|
||||||
|
sg_obtain_scsi_adr() tries to obtain SCSI address parameters.
|
||||||
|
|
||||||
|
|
||||||
|
Porting hints are marked by the text "PORTING:".
|
||||||
|
Send feedback to libburn-hackers@pykix.org .
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** PORTING : ------- OS dependent headers and definitions ------ */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <camlib.h>
|
||||||
|
#include <cam/scsi/scsi_message.h>
|
||||||
|
#include <cam/scsi/scsi_pass.h>
|
||||||
|
|
||||||
|
#include <err.h> /* XXX */
|
||||||
|
|
||||||
|
|
||||||
|
/** PORTING : ------ libburn portable headers and definitions ----- */
|
||||||
|
|
||||||
|
#include "transport.h"
|
||||||
|
#include "drive.h"
|
||||||
|
#include "sg.h"
|
||||||
|
#include "spc.h"
|
||||||
|
#include "mmc.h"
|
||||||
|
#include "sbc.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "toc.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
|
||||||
|
/* is in portable part of libburn */
|
||||||
|
int burn_drive_is_banned(char *device_address);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
/* ts A61115: Private functions. Port only if needed by public functions */
|
||||||
|
/* (Public functions are listed below) */
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/* Helper function for scsi_give_next_adr() */
|
||||||
|
static int sg_init_enumerator(burn_drive_enumerator_t *idx)
|
||||||
|
{
|
||||||
|
idx->skip_device = 0;
|
||||||
|
|
||||||
|
if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
|
||||||
|
warn("couldn't open %s", XPT_DEVICE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(&(idx->ccb), sizeof(union ccb));
|
||||||
|
|
||||||
|
idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
||||||
|
idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
||||||
|
idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
||||||
|
|
||||||
|
idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
||||||
|
idx->bufsize = sizeof(struct dev_match_result) * 100;
|
||||||
|
idx->ccb.cdm.match_buf_len = idx->bufsize;
|
||||||
|
idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize);
|
||||||
|
if (idx->ccb.cdm.matches == NULL) {
|
||||||
|
warnx("can't malloc memory for matches");
|
||||||
|
close(idx->fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
idx->ccb.cdm.num_matches = 0;
|
||||||
|
idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We fetch all nodes, since we display most of them in the default
|
||||||
|
* case, and all in the verbose case.
|
||||||
|
*/
|
||||||
|
idx->ccb.cdm.num_patterns = 0;
|
||||||
|
idx->ccb.cdm.pattern_buf_len = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Helper function for scsi_give_next_adr() */
|
||||||
|
static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We do the ioctl multiple times if necessary, in case there are
|
||||||
|
* more than 100 nodes in the EDT.
|
||||||
|
*/
|
||||||
|
if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
|
||||||
|
warn("error sending CAMIOCOMMAND ioctl");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
|
||||||
|
|| ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
|
||||||
|
&& (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
||||||
|
warnx("got CAM error %#x, CDM error %d\n",
|
||||||
|
idx->ccb.ccb_h.status, idx->ccb.cdm.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int sg_close_drive(struct burn_drive * d)
|
||||||
|
{
|
||||||
|
if (d->cam != NULL) {
|
||||||
|
cam_close_device(d->cam);
|
||||||
|
d->cam = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
/* PORTING: Private functions which contain publicly needed functionality. */
|
||||||
|
/* Their portable part must be performed. So it is probably best */
|
||||||
|
/* to replace the non-portable part and to call these functions */
|
||||||
|
/* in your port, too. */
|
||||||
|
/* ----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/** Wraps a detected drive into libburn structures and hands it over to
|
||||||
|
libburn drive list.
|
||||||
|
*/
|
||||||
|
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct burn_drive out;
|
||||||
|
|
||||||
|
/* General libburn drive setup */
|
||||||
|
burn_setup_drive(&out, fname);
|
||||||
|
|
||||||
|
/* This transport adapter uses SCSI-family commands and models
|
||||||
|
(seems the adapter would know better than its boss, if ever) */
|
||||||
|
ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no,
|
||||||
|
target_no, lun_no, 0);
|
||||||
|
if (ret<=0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* PORTING: ------------------- non portable part --------------- */
|
||||||
|
|
||||||
|
/* Operating system adapter is CAM */
|
||||||
|
/* Adapter specific handles and data */
|
||||||
|
out.cam = NULL;
|
||||||
|
|
||||||
|
/* PORTING: ---------------- end of non portable part ------------ */
|
||||||
|
|
||||||
|
/* Adapter specific functions with standardized names */
|
||||||
|
out.grab = sg_grab;
|
||||||
|
out.release = sg_release;
|
||||||
|
out.drive_is_open = sg_drive_is_open;
|
||||||
|
out.issue_command = sg_issue_command;
|
||||||
|
/* Finally register drive and inquire drive information */
|
||||||
|
burn_drive_finish_enum(&out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61115 */
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
/* PORTING: Public functions. These MUST be ported. */
|
||||||
|
/* ------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the next index number and the next enumerated drive address.
|
||||||
|
The enumeration has to cover all available and accessible drives. It is
|
||||||
|
allowed to return addresses of drives which are not available but under
|
||||||
|
some (even exotic) circumstances could be available. It is on the other
|
||||||
|
hand allowed, only to hand out addresses which can really be used right
|
||||||
|
in the moment of this call. (This implementation chooses the latter.)
|
||||||
|
@param idx An opaque handle. Make no own theories about it.
|
||||||
|
@param adr Takes the reply
|
||||||
|
@param adr_size Gives maximum size of reply including final 0
|
||||||
|
@param initialize 1 = start new,
|
||||||
|
0 = continue, use no other values for now
|
||||||
|
-1 = finish
|
||||||
|
@return 1 = reply is a valid address , 0 = no further address available
|
||||||
|
-1 = severe error (e.g. adr_size too small)
|
||||||
|
*/
|
||||||
|
int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
||||||
|
char adr[], int adr_size, int initialize)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (initialize == 1) {
|
||||||
|
ret = sg_init_enumerator(idx);
|
||||||
|
if (ret<=0)
|
||||||
|
return ret;
|
||||||
|
} else if (initialize == -1) {
|
||||||
|
if(idx->fd != -1)
|
||||||
|
close(idx->fd);
|
||||||
|
idx->fd = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try_item:; /* This spaghetti loop keeps the number of tabs small */
|
||||||
|
|
||||||
|
/* Loop content from old scsi_enumerate_drives() */
|
||||||
|
|
||||||
|
while (idx->i >= idx->ccb.cdm.num_matches) {
|
||||||
|
ret = sg_next_enumeration_buffer(idx);
|
||||||
|
if (ret<=0)
|
||||||
|
return -1;
|
||||||
|
if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP)
|
||||||
|
&& (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) )
|
||||||
|
return 0;
|
||||||
|
idx->i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (idx->ccb.cdm.matches[idx->i].type) {
|
||||||
|
case DEV_MATCH_BUS:
|
||||||
|
break;
|
||||||
|
case DEV_MATCH_DEVICE: {
|
||||||
|
struct device_match_result* result;
|
||||||
|
|
||||||
|
result = &(idx->ccb.cdm.matches[i].result.device_result);
|
||||||
|
if (result->flags & DEV_RESULT_UNCONFIGURED)
|
||||||
|
idx->skip_device = 1;
|
||||||
|
else
|
||||||
|
idx->skip_device = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEV_MATCH_PERIPH: {
|
||||||
|
struct periph_match_result* result;
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
result = &(idx->ccb.cdm.matches[i].result.periph_result);
|
||||||
|
if (idx->skip_device ||
|
||||||
|
strcmp(result->periph_name, "pass") == 0)
|
||||||
|
break;
|
||||||
|
snprintf(buf, sizeof (buf), "/dev/%s%d",
|
||||||
|
result->periph_name, result->unit_number);
|
||||||
|
if(adr_size <= strlen(buf)
|
||||||
|
return -1;
|
||||||
|
strcpy(adr, buf);
|
||||||
|
|
||||||
|
/* Found next enumerable address */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
/* printf(stderr, "unknown match type\n"); */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
(idx->i)++;
|
||||||
|
goto try_item; /* Regular function exit is return 1 above */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Brings all available, not-whitelist-banned, and accessible drives into
|
||||||
|
libburn's list of drives.
|
||||||
|
*/
|
||||||
|
int scsi_enumerate_drives(void)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int initialize = 1;
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||||
|
initialize = 0;
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (burn_drive_is_banned(buf))
|
||||||
|
continue;
|
||||||
|
enumerate_common(buf, idx.result->path_id, idx.result->path_id,
|
||||||
|
0, idx.result->target_id,
|
||||||
|
idx.result->target_lun);
|
||||||
|
}
|
||||||
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Tells wether libburn has the given drive in use or exclusively reserved.
|
||||||
|
If it is "open" then libburn will eventually call sg_release() on it when
|
||||||
|
it is time to give up usage resp. reservation.
|
||||||
|
*/
|
||||||
|
/** Published as burn_drive.drive_is_open() */
|
||||||
|
int sg_drive_is_open(struct burn_drive * d)
|
||||||
|
{
|
||||||
|
return (d->cam != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Opens the drive for SCSI commands and - if burn activities are prone
|
||||||
|
to external interference on your system - obtains an exclusive access lock
|
||||||
|
on the drive. (Note: this is not physical tray locking.)
|
||||||
|
A drive that has been opened with sg_grab() will eventually be handed
|
||||||
|
over to sg_release() for closing and unreserving.
|
||||||
|
*/
|
||||||
|
int sg_grab(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
struct cam_device *cam;
|
||||||
|
|
||||||
|
if(d->cam != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cam = cam_open_device(d->devname, O_RDWR);
|
||||||
|
if (cam == NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
||||||
|
0x00020003,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Could not grab drive", 0/*os_errno*/, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
d->cam = cam;
|
||||||
|
fcntl(cam->fd, F_SETOWN, getpid());
|
||||||
|
d->released = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** PORTING: Is mainly about the call to sg_close_drive() and wether it
|
||||||
|
implements the demanded functionality.
|
||||||
|
*/
|
||||||
|
/** Gives up the drive for SCSI commands and releases eventual access locks.
|
||||||
|
(Note: this is not physical tray locking.)
|
||||||
|
*/
|
||||||
|
int sg_release(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
if (d->cam == NULL) {
|
||||||
|
burn_print(1, "release an ungrabbed drive. die\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sg_close_drive(d);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sends a SCSI command to the drive, receives reply and evaluates wether
|
||||||
|
the command succeeded or shall be retried or finally failed.
|
||||||
|
Returned SCSI errors shall not lead to a return value indicating failure.
|
||||||
|
The callers get notified by c->error. An SCSI failure which leads not to
|
||||||
|
a retry shall be notified via scsi_notify_error().
|
||||||
|
The Libburn_log_sg_commandS facility might be of help when problems with
|
||||||
|
a drive have to be examined. It shall stay disabled for normal use.
|
||||||
|
@return: 1 success , <=0 failure
|
||||||
|
*/
|
||||||
|
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||||
|
{
|
||||||
|
int done = 0;
|
||||||
|
int err;
|
||||||
|
union ccb *ccb;
|
||||||
|
|
||||||
|
if (d->cam == NULL) {
|
||||||
|
c->error = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->error = 0;
|
||||||
|
|
||||||
|
ccb = cam_getccb(d->cam);
|
||||||
|
cam_fill_csio(&ccb->csio,
|
||||||
|
1, /* retries */
|
||||||
|
NULL, /* cbfncp */
|
||||||
|
CAM_DEV_QFRZDIS, /* flags */
|
||||||
|
MSG_SIMPLE_Q_TAG, /* tag_action */
|
||||||
|
NULL, /* data_ptr */
|
||||||
|
0, /* dxfer_len */
|
||||||
|
sizeof (ccb->csio.sense_data), /* sense_len */
|
||||||
|
0, /* cdb_len */
|
||||||
|
30*1000); /* timeout */
|
||||||
|
switch (c->dir) {
|
||||||
|
case TO_DRIVE:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
|
||||||
|
break;
|
||||||
|
case FROM_DRIVE:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_IN;
|
||||||
|
break;
|
||||||
|
case NO_TRANSFER:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccb->csio.cdb_len = c->oplen;
|
||||||
|
memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
|
||||||
|
|
||||||
|
memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data));
|
||||||
|
|
||||||
|
if (c->page) {
|
||||||
|
ccb->csio.data_ptr = c->page->data;
|
||||||
|
if (c->dir == FROM_DRIVE) {
|
||||||
|
ccb->csio.dxfer_len = BUFFER_SIZE;
|
||||||
|
/* touch page so we can use valgrind */
|
||||||
|
memset(c->page->data, 0, BUFFER_SIZE);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* ts A61115: removed a ssert() */
|
||||||
|
if(c->page->bytes <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ccb->csio.dxfer_len = c->page->bytes;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ccb->csio.data_ptr = NULL;
|
||||||
|
ccb->csio.dxfer_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
err = cam_send_ccb(d->cam, ccb);
|
||||||
|
if (err == -1) {
|
||||||
|
libdax_msgs_submit(libdax_messenger,
|
||||||
|
d->global_index, 0x0002010c,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Failed to transfer command to drive",
|
||||||
|
errno, 0);
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
sg_close_drive(d);
|
||||||
|
d->released = 1;
|
||||||
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
|
c->error = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* XXX */
|
||||||
|
memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
|
||||||
|
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
if (!c->retry) {
|
||||||
|
c->error = 1;
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
switch (scsi_error(d, c->sense, 0)) {
|
||||||
|
case RETRY:
|
||||||
|
done = 0;
|
||||||
|
break;
|
||||||
|
case FAIL:
|
||||||
|
done = 1;
|
||||||
|
c->error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
} while (!done);
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Tries to obtain SCSI address parameters.
|
||||||
|
@return 1 is success , 0 is failure
|
||||||
|
*/
|
||||||
|
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||||
|
int *target_no, int *lun_no)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int initialize = 1;
|
||||||
|
char buf[64];
|
||||||
|
struct periph_match_result* result;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||||
|
initialize = 0;
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (strcmp(adr, buf) != 0)
|
||||||
|
continue;
|
||||||
|
result = &(idx->ccb.cdm.matches[i].result.periph_result);
|
||||||
|
*bus_no = result->path_id;
|
||||||
|
*host_no = result->path_id;
|
||||||
|
*channel_no = 0;
|
||||||
|
*target_no = result->target_id
|
||||||
|
*lun_no = result->target_lun;
|
||||||
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Tells wether a text is a persistent address as listed by the enumeration
|
||||||
|
functions.
|
||||||
|
*/
|
||||||
|
int sg_is_enumerable_adr(char* adr)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int initialize = 1;
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize);
|
||||||
|
initialize = 0;
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (strcmp(adr, buf) == 0) {
|
||||||
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
600
libburn/sg-freebsd.c
Normal file
600
libburn/sg-freebsd.c
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <camlib.h>
|
||||||
|
#include <cam/scsi/scsi_message.h>
|
||||||
|
#include <cam/scsi/scsi_pass.h>
|
||||||
|
|
||||||
|
#include <err.h> /* XXX */
|
||||||
|
|
||||||
|
|
||||||
|
#include "transport.h"
|
||||||
|
#include "drive.h"
|
||||||
|
#include "sg.h"
|
||||||
|
#include "spc.h"
|
||||||
|
#include "mmc.h"
|
||||||
|
#include "sbc.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "toc.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
struct burn_drive_enumeration_state {
|
||||||
|
int fd;
|
||||||
|
union ccb ccb;
|
||||||
|
unsigned int i;
|
||||||
|
int skip_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no);
|
||||||
|
|
||||||
|
/* ts A51221 */
|
||||||
|
int burn_drive_is_banned(char *device_address);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A60821
|
||||||
|
<<< debug: for tracing calls which might use open drive fds */
|
||||||
|
int mmc_function_spy(char * text);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61021 : Moved most code from scsi_enumerate_drives under
|
||||||
|
sg_give_next_adr() */
|
||||||
|
/* Some helper functions for scsi_give_next_adr() */
|
||||||
|
|
||||||
|
static int sg_init_enumerator(burn_drive_enumerator_t *idx_)
|
||||||
|
{
|
||||||
|
struct burn_drive_enumeration_state *idx;
|
||||||
|
int bufsize;
|
||||||
|
|
||||||
|
idx = malloc(sizeof(*idx));
|
||||||
|
if (idx == NULL) {
|
||||||
|
warnx("can't malloc memory for enumerator");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
idx->skip_device = 0;
|
||||||
|
|
||||||
|
if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) {
|
||||||
|
warn("couldn't open %s", XPT_DEVICE);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(&(idx->ccb), sizeof(union ccb));
|
||||||
|
|
||||||
|
idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
||||||
|
idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
||||||
|
idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
||||||
|
|
||||||
|
idx->ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
||||||
|
bufsize = sizeof(struct dev_match_result) * 100;
|
||||||
|
idx->ccb.cdm.match_buf_len = bufsize;
|
||||||
|
idx->ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
|
||||||
|
if (idx->ccb.cdm.matches == NULL) {
|
||||||
|
warnx("can't malloc memory for matches");
|
||||||
|
close(idx->fd);
|
||||||
|
free(idx);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
idx->ccb.cdm.num_matches = 0;
|
||||||
|
idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We fetch all nodes, since we display most of them in the default
|
||||||
|
* case, and all in the verbose case.
|
||||||
|
*/
|
||||||
|
idx->ccb.cdm.num_patterns = 0;
|
||||||
|
idx->ccb.cdm.pattern_buf_len = 0;
|
||||||
|
|
||||||
|
*idx_ = idx;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sg_destroy_enumerator(burn_drive_enumerator_t *idx_)
|
||||||
|
{
|
||||||
|
struct burn_drive_enumeration_state *idx = *idx_;
|
||||||
|
|
||||||
|
if(idx->fd != -1)
|
||||||
|
close(idx->fd);
|
||||||
|
|
||||||
|
free(idx->ccb.cdm.matches);
|
||||||
|
free(idx);
|
||||||
|
|
||||||
|
*idx_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx_)
|
||||||
|
{
|
||||||
|
struct burn_drive_enumeration_state *idx = *idx_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do the ioctl multiple times if necessary, in case there are
|
||||||
|
* more than 100 nodes in the EDT.
|
||||||
|
*/
|
||||||
|
if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) {
|
||||||
|
warn("error sending CAMIOCOMMAND ioctl");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((idx->ccb.ccb_h.status != CAM_REQ_CMP)
|
||||||
|
|| ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST)
|
||||||
|
&& (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
||||||
|
warnx("got CAM error %#x, CDM error %d\n",
|
||||||
|
idx->ccb.ccb_h.status, idx->ccb.cdm.status);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the next index number and the next enumerated drive address.
|
||||||
|
@param idx An opaque handle. Make no own theories about it.
|
||||||
|
@param adr Takes the reply
|
||||||
|
@param adr_size Gives maximum size of reply including final 0
|
||||||
|
@param initialize 1 = start new,
|
||||||
|
0 = continue, use no other values for now
|
||||||
|
-1 = finish
|
||||||
|
@return 1 = reply is a valid address , 0 = no further address available
|
||||||
|
-1 = severe error (e.g. adr_size too small)
|
||||||
|
*/
|
||||||
|
int sg_give_next_adr(burn_drive_enumerator_t *idx_,
|
||||||
|
char adr[], int adr_size, int initialize)
|
||||||
|
{
|
||||||
|
struct burn_drive_enumeration_state *idx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (initialize == 1) {
|
||||||
|
ret = sg_init_enumerator(idx_);
|
||||||
|
if (ret<=0)
|
||||||
|
return ret;
|
||||||
|
} else if (initialize == -1) {
|
||||||
|
sg_destroy_enumerator(idx_);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = *idx_;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (idx->i >= idx->ccb.cdm.num_matches) {
|
||||||
|
ret = sg_next_enumeration_buffer(idx_);
|
||||||
|
if (ret<=0)
|
||||||
|
return -1;
|
||||||
|
idx->i = 0;
|
||||||
|
} else
|
||||||
|
(idx->i)++;
|
||||||
|
|
||||||
|
while (idx->i < idx->ccb.cdm.num_matches) {
|
||||||
|
switch (idx->ccb.cdm.matches[idx->i].type) {
|
||||||
|
case DEV_MATCH_BUS:
|
||||||
|
break;
|
||||||
|
case DEV_MATCH_DEVICE: {
|
||||||
|
struct device_match_result* result;
|
||||||
|
|
||||||
|
result = &(idx->ccb.cdm.matches[idx->i].result.device_result);
|
||||||
|
if (result->flags & DEV_RESULT_UNCONFIGURED)
|
||||||
|
idx->skip_device = 1;
|
||||||
|
else
|
||||||
|
idx->skip_device = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DEV_MATCH_PERIPH: {
|
||||||
|
struct periph_match_result* result;
|
||||||
|
|
||||||
|
result = &(idx->ccb.cdm.matches[idx->i].result.periph_result);
|
||||||
|
if (idx->skip_device ||
|
||||||
|
strcmp(result->periph_name, "pass") == 0)
|
||||||
|
break;
|
||||||
|
ret = snprintf(adr, adr_size, "/dev/%s%d",
|
||||||
|
result->periph_name, result->unit_number);
|
||||||
|
if(ret >= adr_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Found next enumerable address */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
/* printf(stderr, "unknown match type\n"); */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(idx->i)++;
|
||||||
|
}
|
||||||
|
} while ((idx->ccb.ccb_h.status == CAM_REQ_CMP)
|
||||||
|
&& (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sg_is_enumerable_adr(char* adr)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int ret;
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
ret = sg_init_enumerator(&idx);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (strcmp(adr, buf) == 0) {
|
||||||
|
sg_destroy_enumerator(&idx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sg_destroy_enumerator(&idx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Try to obtain SCSI address parameters.
|
||||||
|
@return 1 is success , 0 is failure
|
||||||
|
*/
|
||||||
|
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||||
|
int *target_no, int *lun_no)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int ret;
|
||||||
|
char buf[64];
|
||||||
|
struct periph_match_result* result;
|
||||||
|
|
||||||
|
ret = sg_init_enumerator(&idx);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (strcmp(path, buf) == 0) {
|
||||||
|
result = &(idx->ccb.cdm.matches[idx->i].result.periph_result);
|
||||||
|
*bus_no = result->path_id;
|
||||||
|
*host_no = result->path_id;
|
||||||
|
*channel_no = 0;
|
||||||
|
*target_no = result->target_id;
|
||||||
|
*lun_no = result->target_lun;
|
||||||
|
sg_destroy_enumerator(&idx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sg_destroy_enumerator(&idx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sg_close_drive(struct burn_drive * d)
|
||||||
|
{
|
||||||
|
if (d->cam != NULL) {
|
||||||
|
cam_close_device(d->cam);
|
||||||
|
d->cam = NULL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sg_drive_is_open(struct burn_drive * d)
|
||||||
|
{
|
||||||
|
return (d->cam != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int scsi_enumerate_drives(void)
|
||||||
|
{
|
||||||
|
burn_drive_enumerator_t idx;
|
||||||
|
int ret;
|
||||||
|
char buf[64];
|
||||||
|
struct periph_match_result* result;
|
||||||
|
|
||||||
|
ret = sg_init_enumerator(&idx);
|
||||||
|
if (ret <= 0)
|
||||||
|
return 0;
|
||||||
|
while(1) {
|
||||||
|
ret = sg_give_next_adr(&idx, buf, sizeof(buf), 0);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
if (burn_drive_is_banned(buf))
|
||||||
|
continue;
|
||||||
|
result = &idx->ccb.cdm.matches[idx->i].result.periph_result;
|
||||||
|
enumerate_common(buf, result->path_id, result->path_id,
|
||||||
|
0, result->target_id,
|
||||||
|
result->target_lun);
|
||||||
|
}
|
||||||
|
sg_destroy_enumerator(&idx);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef Scsi_freebsd_make_own_enumeratE
|
||||||
|
|
||||||
|
/* ts A61021: The old version which mixes SCSI and operating system adapter
|
||||||
|
*/
|
||||||
|
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no)
|
||||||
|
{
|
||||||
|
struct burn_drive *t;
|
||||||
|
struct burn_drive out;
|
||||||
|
|
||||||
|
/* ts A60923 */
|
||||||
|
out.bus_no = bus_no;
|
||||||
|
out.host = host_no;
|
||||||
|
out.id = target_no;
|
||||||
|
out.channel = channel_no;
|
||||||
|
out.lun = lun_no;
|
||||||
|
|
||||||
|
out.devname = burn_strdup(fname);
|
||||||
|
out.cam = NULL;
|
||||||
|
|
||||||
|
out.start_lba= -2000000000;
|
||||||
|
out.end_lba= -2000000000;
|
||||||
|
out.read_atip = mmc_read_atip;
|
||||||
|
|
||||||
|
out.grab = sg_grab;
|
||||||
|
out.release = sg_release;
|
||||||
|
out.drive_is_open= sg_drive_is_open;
|
||||||
|
out.issue_command = sg_issue_command;
|
||||||
|
out.getcaps = spc_getcaps;
|
||||||
|
out.released = 1;
|
||||||
|
out.status = BURN_DISC_UNREADY;
|
||||||
|
|
||||||
|
out.eject = sbc_eject;
|
||||||
|
out.load = sbc_load;
|
||||||
|
out.lock = spc_prevent;
|
||||||
|
out.unlock = spc_allow;
|
||||||
|
out.read_disc_info = spc_sense_write_params;
|
||||||
|
out.get_erase_progress = spc_get_erase_progress;
|
||||||
|
out.test_unit_ready = spc_test_unit_ready;
|
||||||
|
out.probe_write_modes = spc_probe_write_modes;
|
||||||
|
out.read_toc = mmc_read_toc;
|
||||||
|
out.write = mmc_write;
|
||||||
|
out.erase = mmc_erase;
|
||||||
|
out.read_sectors = mmc_read_sectors;
|
||||||
|
out.perform_opc = mmc_perform_opc;
|
||||||
|
out.set_speed = mmc_set_speed;
|
||||||
|
out.send_parameters = spc_select_error_params;
|
||||||
|
out.send_write_parameters = spc_select_write_params;
|
||||||
|
out.send_cue_sheet = mmc_send_cue_sheet;
|
||||||
|
out.sync_cache = mmc_sync_cache;
|
||||||
|
out.get_nwa = mmc_get_nwa;
|
||||||
|
out.close_disc = mmc_close_disc;
|
||||||
|
out.close_session = mmc_close_session;
|
||||||
|
out.close_track_session = mmc_close;
|
||||||
|
out.read_buffer_capacity = mmc_read_buffer_capacity;
|
||||||
|
out.idata = malloc(sizeof(struct burn_scsi_inquiry_data));
|
||||||
|
out.idata->valid = 0;
|
||||||
|
out.mdata = malloc(sizeof(struct scsi_mode_data));
|
||||||
|
out.mdata->valid = 0;
|
||||||
|
if (out.idata == NULL || out.mdata == NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020108,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Could not allocate new drive object", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memset(&out.params, 0, sizeof(struct params));
|
||||||
|
t = burn_drive_register(&out);
|
||||||
|
|
||||||
|
/* ts A60821
|
||||||
|
<<< debug: for tracing calls which might use open drive fds */
|
||||||
|
mmc_function_spy("enumerate_common : -------- doing grab");
|
||||||
|
|
||||||
|
/* try to get the drive info */
|
||||||
|
if (t->grab(t)) {
|
||||||
|
burn_print(2, "getting drive info\n");
|
||||||
|
t->getcaps(t);
|
||||||
|
t->unlock(t);
|
||||||
|
t->released = 1;
|
||||||
|
} else {
|
||||||
|
burn_print(2, "unable to grab new located drive\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A60821
|
||||||
|
<<< debug: for tracing calls which might use open drive fds */
|
||||||
|
mmc_function_spy("enumerate_common : ----- would release ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* Scsi_freebsd_make_own_enumeratE */
|
||||||
|
|
||||||
|
/* The new, more concise version of enumerate_common */
|
||||||
|
static void enumerate_common(char *fname, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct burn_drive out;
|
||||||
|
|
||||||
|
/* General libburn drive setup */
|
||||||
|
burn_setup_drive(&out, fname);
|
||||||
|
|
||||||
|
/* This transport adapter uses SCSI-family commands and models
|
||||||
|
(seems the adapter would know better than its boss, if ever) */
|
||||||
|
ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no,
|
||||||
|
target_no, lun_no, 0);
|
||||||
|
if (ret<=0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Operating system adapter is CAM */
|
||||||
|
/* Adapter specific handles and data */
|
||||||
|
out.cam = NULL;
|
||||||
|
/* Adapter specific functions */
|
||||||
|
out.grab = sg_grab;
|
||||||
|
out.release = sg_release;
|
||||||
|
out.drive_is_open = sg_drive_is_open;
|
||||||
|
out.issue_command = sg_issue_command;
|
||||||
|
|
||||||
|
/* Finally register drive and inquire drive information */
|
||||||
|
burn_drive_finish_enum(&out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ! Scsi_freebsd_make_own_enumeratE */
|
||||||
|
|
||||||
|
/* ts A61021: do not believe this:
|
||||||
|
we use the sg reference count to decide whether we can use the
|
||||||
|
drive or not.
|
||||||
|
if refcount is not one, drive is open somewhere else.
|
||||||
|
*/
|
||||||
|
int sg_grab(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
struct cam_device *cam;
|
||||||
|
|
||||||
|
mmc_function_spy("sg_grab");
|
||||||
|
|
||||||
|
if (burn_drive_is_open(d)) {
|
||||||
|
d->released = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cam = cam_open_device(d->devname, O_RDWR);
|
||||||
|
if (cam == NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Could not grab drive", 0/*os_errno*/, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
|
||||||
|
count = 1;
|
||||||
|
if (1 == count) {
|
||||||
|
d->cam = cam;
|
||||||
|
fcntl(cam->fd, F_SETOWN, getpid());
|
||||||
|
d->released = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
burn_print(1, "could not acquire drive - already open\n");
|
||||||
|
sg_close_drive(d);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
non zero return means you still have the drive and it's not
|
||||||
|
in a state to be released? (is that even possible?)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int sg_release(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
mmc_function_spy("sg_release");
|
||||||
|
|
||||||
|
if (d->cam == NULL) {
|
||||||
|
burn_print(1, "release an ungrabbed drive. die\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmc_function_spy("sg_release ----------- closing.");
|
||||||
|
|
||||||
|
sg_close_drive(d);
|
||||||
|
d->released = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sg_issue_command(struct burn_drive *d, struct command *c)
|
||||||
|
{
|
||||||
|
int done = 0;
|
||||||
|
int err;
|
||||||
|
union ccb *ccb;
|
||||||
|
|
||||||
|
char buf[161];
|
||||||
|
snprintf(buf, sizeof (buf), "sg_issue_command d->cam=%p d->released=%d",
|
||||||
|
(void*)d->cam, d->released);
|
||||||
|
mmc_function_spy(buf);
|
||||||
|
|
||||||
|
if (d->cam == NULL) {
|
||||||
|
c->error = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->error = 0;
|
||||||
|
|
||||||
|
ccb = cam_getccb(d->cam);
|
||||||
|
cam_fill_csio(&ccb->csio,
|
||||||
|
1, /* retries */
|
||||||
|
NULL, /* cbfncp */
|
||||||
|
CAM_DEV_QFRZDIS, /* flags */
|
||||||
|
MSG_SIMPLE_Q_TAG, /* tag_action */
|
||||||
|
NULL, /* data_ptr */
|
||||||
|
0, /* dxfer_len */
|
||||||
|
sizeof (ccb->csio.sense_data), /* sense_len */
|
||||||
|
0, /* cdb_len */
|
||||||
|
30*1000); /* timeout */
|
||||||
|
switch (c->dir) {
|
||||||
|
case TO_DRIVE:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_OUT;
|
||||||
|
break;
|
||||||
|
case FROM_DRIVE:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_IN;
|
||||||
|
break;
|
||||||
|
case NO_TRANSFER:
|
||||||
|
ccb->csio.ccb_h.flags |= CAM_DIR_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccb->csio.cdb_len = c->oplen;
|
||||||
|
memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen);
|
||||||
|
|
||||||
|
memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data));
|
||||||
|
|
||||||
|
if (c->page) {
|
||||||
|
ccb->csio.data_ptr = c->page->data;
|
||||||
|
if (c->dir == FROM_DRIVE) {
|
||||||
|
ccb->csio.dxfer_len = BUFFER_SIZE;
|
||||||
|
/* touch page so we can use valgrind */
|
||||||
|
memset(c->page->data, 0, BUFFER_SIZE);
|
||||||
|
} else {
|
||||||
|
assert(c->page->bytes > 0);
|
||||||
|
ccb->csio.dxfer_len = c->page->bytes;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ccb->csio.data_ptr = NULL;
|
||||||
|
ccb->csio.dxfer_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
err = cam_send_ccb(d->cam, ccb);
|
||||||
|
if (err == -1) {
|
||||||
|
libdax_msgs_submit(libdax_messenger,
|
||||||
|
d->global_index, 0x0002010c,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Failed to transfer command to drive",
|
||||||
|
errno, 0);
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
sg_close_drive(d);
|
||||||
|
d->released = 1;
|
||||||
|
d->busy = BURN_DRIVE_IDLE;
|
||||||
|
c->error = 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* XXX */
|
||||||
|
memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len);
|
||||||
|
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||||
|
if (!c->retry) {
|
||||||
|
c->error = 1;
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
switch (scsi_error(d, c->sense, 0)) {
|
||||||
|
case RETRY:
|
||||||
|
done = 0;
|
||||||
|
break;
|
||||||
|
case FAIL:
|
||||||
|
done = 1;
|
||||||
|
c->error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
} while (!done);
|
||||||
|
cam_freeccb(ccb);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
1104
libburn/sg-linux.c
Normal file
1104
libburn/sg-linux.c
Normal file
File diff suppressed because it is too large
Load Diff
499
libburn/sg.c
499
libburn/sg.c
@ -1,500 +1,17 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
|
||||||
|
|
||||||
#include <assert.h>
|
/* sg.c
|
||||||
#include <errno.h>
|
Switcher for operating system dependent transport level modules of libburn.
|
||||||
#include <unistd.h>
|
Copyright (C) 2006 Thomas Schmitt <scdbackup@gmx.net>, provided under GPL
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <linux/hdreg.h>
|
|
||||||
|
|
||||||
#include "transport.h"
|
|
||||||
#include "drive.h"
|
|
||||||
#include "sg.h"
|
|
||||||
#include "spc.h"
|
|
||||||
#include "mmc.h"
|
|
||||||
#include "sbc.h"
|
|
||||||
#include "debug.h"
|
|
||||||
#include "toc.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
static void enumerate_common(char *fname);
|
|
||||||
|
|
||||||
/* ts A51221 */
|
|
||||||
int burn_drive_is_banned(char *device_address);
|
|
||||||
|
|
||||||
/* ts A60813 : storage objects are in libburn/init.c
|
|
||||||
wether to use O_EXCL
|
|
||||||
wether to use O_NOBLOCK with open(2) on devices
|
|
||||||
wether to take O_EXCL rejection as fatal error */
|
|
||||||
extern int burn_sg_open_o_excl;
|
|
||||||
extern int burn_sg_open_o_nonblock;
|
|
||||||
extern int burn_sg_open_abort_busy;
|
|
||||||
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
int mmc_function_spy(char * text);
|
|
||||||
|
|
||||||
|
|
||||||
static int sgio_test(int fd)
|
|
||||||
{
|
|
||||||
unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 };
|
|
||||||
sg_io_hdr_t s;
|
|
||||||
|
|
||||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
|
||||||
s.interface_id = 'S';
|
|
||||||
s.dxfer_direction = SG_DXFER_NONE;
|
|
||||||
s.cmd_len = 6;
|
|
||||||
s.cmdp = test_ops;
|
|
||||||
s.timeout = 12345;
|
|
||||||
return ioctl(fd, SG_IO, &s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ata_enumerate(void)
|
|
||||||
{
|
|
||||||
struct hd_driveid tm;
|
|
||||||
int i, fd;
|
|
||||||
char fname[10];
|
|
||||||
|
|
||||||
/* ts A60813 */
|
|
||||||
int open_mode = O_RDWR;
|
|
||||||
|
|
||||||
/* ts A60813
|
|
||||||
O_EXCL with block devices is an unpublished feature
|
|
||||||
of Linux kernels. Possibly introduced 2002.
|
|
||||||
It can only be used if libburn stops opening several
|
|
||||||
file descriptor on the same block device.
|
|
||||||
See comment in sg_grab() */
|
|
||||||
if(burn_sg_open_o_excl)
|
|
||||||
open_mode |= O_EXCL;
|
|
||||||
/* ts A60813
|
|
||||||
O_NONBLOCK was already hardcoded in ata_ but not in sg_.
|
|
||||||
There must be some reason for this. So O_NONBLOCK is
|
|
||||||
default mode for both now. Disable on own risk. */
|
|
||||||
if(burn_sg_open_o_nonblock)
|
|
||||||
open_mode |= O_NONBLOCK;
|
|
||||||
|
|
||||||
for (i = 0; i < 26; i++) {
|
|
||||||
sprintf(fname, "/dev/hd%c", 'a' + i);
|
|
||||||
/* open O_RDWR so we don't think read only drives are
|
|
||||||
in some way useful
|
|
||||||
*/
|
|
||||||
/* ts A51221 */
|
|
||||||
if (burn_drive_is_banned(fname))
|
|
||||||
continue;
|
|
||||||
fd = open(fname, open_mode);
|
|
||||||
if (fd == -1) {
|
|
||||||
/* <<< debugging
|
|
||||||
fprintf(stderr,
|
|
||||||
"\nlibburn: experimental: fname= %s , errno= %d\n",
|
|
||||||
fname,errno);
|
|
||||||
*/
|
|
||||||
/* ts A60814 : i see no way to do this more nicely */
|
|
||||||
if (errno == EBUSY && burn_sg_open_abort_busy) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
|
|
||||||
fname);
|
|
||||||
/* <<< maybe one should plainly exit here */
|
|
||||||
assert("drive busy" == "non fatal");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* found a drive */
|
|
||||||
ioctl(fd, HDIO_GET_IDENTITY, &tm);
|
|
||||||
|
|
||||||
/* not atapi */
|
|
||||||
if (!(tm.config & 0x8000) || (tm.config & 0x4000)) {
|
|
||||||
close(fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if SG_IO fails on an atapi device, we should stop trying to
|
|
||||||
use hd* devices */
|
|
||||||
if (sgio_test(fd) == -1) {
|
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
enumerate_common(fname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sg_enumerate(void)
|
|
||||||
{
|
|
||||||
struct sg_scsi_id sid;
|
|
||||||
int i, fd;
|
|
||||||
char fname[10];
|
|
||||||
|
|
||||||
/* ts A60813 */
|
|
||||||
int open_mode = O_RDWR;
|
|
||||||
|
|
||||||
/* ts A60813
|
|
||||||
O_EXCL with block devices is an unpublished feature
|
|
||||||
of Linux kernels. Possibly introduced 2002.
|
|
||||||
It can only be used if libburn stops opening several
|
|
||||||
file descriptor on the same block device.
|
|
||||||
See comment in sg_grab() */
|
|
||||||
if(burn_sg_open_o_excl)
|
|
||||||
open_mode |= O_EXCL;
|
|
||||||
/* ts A60813
|
|
||||||
O_NONBLOCK was not hardcoded in sg_ but was in ata_.
|
|
||||||
I myself test mainly sg_ and it seems to be ok with
|
|
||||||
O_NONBLOCK. So it should stay default mode. */
|
|
||||||
if(burn_sg_open_o_nonblock)
|
|
||||||
open_mode |= O_NONBLOCK;
|
|
||||||
|
|
||||||
/* <<< debugging
|
|
||||||
fprintf(stderr,
|
|
||||||
"\nlibburn: experimental: o_excl= %d , o_nonblock= %d, abort_on_busy= %d\n",
|
|
||||||
burn_sg_open_o_excl,burn_sg_open_o_nonblock,burn_sg_open_abort_busy);
|
|
||||||
fprintf(stderr,
|
|
||||||
"libburn: experimental: O_EXCL= %d , O_NONBLOCK= %d\n",
|
|
||||||
!!(open_mode&O_EXCL),!!(open_mode&O_NONBLOCK));
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
sprintf(fname, "/dev/sg%d", i);
|
|
||||||
/* open RDWR so we don't accidentally think read only drives
|
|
||||||
are in some way useful
|
|
||||||
*/
|
|
||||||
/* ts A51221 */
|
|
||||||
if (burn_drive_is_banned(fname))
|
|
||||||
continue;
|
|
||||||
fd = open(fname, open_mode);
|
|
||||||
|
|
||||||
if (fd == -1) {
|
#ifdef __FreeBSD__
|
||||||
/* <<< debugging
|
|
||||||
fprintf(stderr,
|
|
||||||
"\n cdrskin: experimental: fname= %s , errno= %d\n",
|
|
||||||
fname,errno);
|
|
||||||
*/
|
|
||||||
/* ts A60814 : i see no way to do this more nicely */
|
|
||||||
if (errno == EBUSY && burn_sg_open_abort_busy) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"\nlibburn: FATAL : Application triggered abort on busy drive '%s'\n",
|
|
||||||
fname);
|
|
||||||
/* <<< maybe one should plainly exit here */
|
|
||||||
assert("drive busy" == "non fatal");
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* found a drive */
|
|
||||||
ioctl(fd, SG_GET_SCSI_ID, &sid);
|
|
||||||
close(fd);
|
|
||||||
if (sid.scsi_type != TYPE_ROM)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
enumerate_common(fname);
|
#include "sg-freebsd.c"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void enumerate_common(char *fname)
|
#else
|
||||||
{
|
|
||||||
struct burn_drive *t;
|
|
||||||
struct burn_drive out;
|
|
||||||
|
|
||||||
out.devname = burn_strdup(fname);
|
#include "sg-linux.c"
|
||||||
out.fd = -1337;
|
|
||||||
|
|
||||||
out.grab = sg_grab;
|
#endif
|
||||||
out.release = sg_release;
|
|
||||||
out.issue_command = sg_issue_command;
|
|
||||||
out.getcaps = spc_getcaps;
|
|
||||||
out.released = 1;
|
|
||||||
out.status = BURN_DISC_UNREADY;
|
|
||||||
|
|
||||||
out.eject = sbc_eject;
|
|
||||||
out.load = sbc_load;
|
|
||||||
out.lock = spc_prevent;
|
|
||||||
out.unlock = spc_allow;
|
|
||||||
out.read_disc_info = spc_sense_write_params;
|
|
||||||
out.get_erase_progress = spc_get_erase_progress;
|
|
||||||
out.test_unit_ready = spc_test_unit_ready;
|
|
||||||
out.probe_write_modes = spc_probe_write_modes;
|
|
||||||
out.read_toc = mmc_read_toc;
|
|
||||||
out.write = mmc_write;
|
|
||||||
out.erase = mmc_erase;
|
|
||||||
out.read_sectors = mmc_read_sectors;
|
|
||||||
out.perform_opc = mmc_perform_opc;
|
|
||||||
out.set_speed = mmc_set_speed;
|
|
||||||
out.send_parameters = spc_select_error_params;
|
|
||||||
out.send_write_parameters = spc_select_write_params;
|
|
||||||
out.send_cue_sheet = mmc_send_cue_sheet;
|
|
||||||
out.sync_cache = mmc_sync_cache;
|
|
||||||
out.get_nwa = mmc_get_nwa;
|
|
||||||
out.close_disc = mmc_close_disc;
|
|
||||||
out.close_session = mmc_close_session;
|
|
||||||
out.idata = malloc(sizeof(struct scsi_inquiry_data));
|
|
||||||
out.idata->valid = 0;
|
|
||||||
out.mdata = malloc(sizeof(struct scsi_mode_data));
|
|
||||||
out.mdata->valid = 0;
|
|
||||||
memset(&out.params, 0, sizeof(struct params));
|
|
||||||
t = burn_drive_register(&out);
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("enumerate_common : -------- doing grab");
|
|
||||||
|
|
||||||
/* try to get the drive info */
|
|
||||||
if (sg_grab(t)) {
|
|
||||||
burn_print(2, "getting drive info\n");
|
|
||||||
t->getcaps(t);
|
|
||||||
t->unlock(t);
|
|
||||||
t->released = 1;
|
|
||||||
} else {
|
|
||||||
burn_print(2, "unable to grab new located drive\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("enumerate_common : ----- would release ");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
we use the sg reference count to decide whether we can use the
|
|
||||||
drive or not.
|
|
||||||
if refcount is not one, drive is open somewhere else.
|
|
||||||
|
|
||||||
ts A60813: this test is too late. O_EXCL is the stronger solution.
|
|
||||||
After all the test was diabled already in icculus.org/burn CVS.
|
|
||||||
*/
|
|
||||||
int sg_grab(struct burn_drive *d)
|
|
||||||
{
|
|
||||||
int fd, count;
|
|
||||||
|
|
||||||
/* ts A60813 */
|
|
||||||
int open_mode = O_RDWR;
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("sg_grab");
|
|
||||||
|
|
||||||
|
|
||||||
/* ts A60813
|
|
||||||
O_EXCL with block devices is an unpublished feature
|
|
||||||
of Linux kernels. Possibly introduced 2002.
|
|
||||||
It can only be used if libburn stops opening several
|
|
||||||
file descriptor on the same block device.
|
|
||||||
See comment below */
|
|
||||||
if(burn_sg_open_o_excl)
|
|
||||||
open_mode |= O_EXCL;
|
|
||||||
|
|
||||||
/* ts A60813
|
|
||||||
O_NONBLOCK was hardcoded here. So it should stay default mode. */
|
|
||||||
if(burn_sg_open_o_nonblock)
|
|
||||||
open_mode |= O_NONBLOCK;
|
|
||||||
|
|
||||||
/* ts A60813
|
|
||||||
After enumeration the drive fd is probably still open.
|
|
||||||
-1337 is the initial value of burn_drive.fd and the value after
|
|
||||||
relase of drive. Unclear why not the official error return
|
|
||||||
value -1 of open(2) war used. */
|
|
||||||
/* ts A60822: was if(d->fd == -1337) { */
|
|
||||||
if(! burn_drive_is_open(d)) {
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("sg_grab ----------- opening");
|
|
||||||
|
|
||||||
fd = open(d->devname, open_mode);
|
|
||||||
} else
|
|
||||||
fd= d->fd;
|
|
||||||
|
|
||||||
assert(fd != -1337);
|
|
||||||
if (-1 != fd) {
|
|
||||||
|
|
||||||
/* ts A60814:
|
|
||||||
according to my experiments this test would work now ! */
|
|
||||||
|
|
||||||
/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/
|
|
||||||
count = 1;
|
|
||||||
if (1 == count) {
|
|
||||||
d->fd = fd;
|
|
||||||
fcntl(fd, F_SETOWN, getpid());
|
|
||||||
d->released = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
burn_print(1, "could not acquire drive - already open\n");
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
burn_print(1, "could not acquire drive\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
non zero return means you still have the drive and it's not
|
|
||||||
in a state to be released? (is that even possible?)
|
|
||||||
*/
|
|
||||||
|
|
||||||
int sg_release(struct burn_drive *d)
|
|
||||||
{
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("sg_release");
|
|
||||||
|
|
||||||
if (d->fd < 1) {
|
|
||||||
burn_print(1, "release an ungrabbed drive. die\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
mmc_function_spy("sg_release ----------- closing");
|
|
||||||
|
|
||||||
close(d->fd);
|
|
||||||
d->fd = -1337;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sg_issue_command(struct burn_drive *d, struct command *c)
|
|
||||||
{
|
|
||||||
int done = 0;
|
|
||||||
int err;
|
|
||||||
sg_io_hdr_t s;
|
|
||||||
|
|
||||||
|
|
||||||
/* ts A60821
|
|
||||||
<<< debug: for tracing calls which might use open drive fds */
|
|
||||||
char buf[161];
|
|
||||||
sprintf(buf,"sg_issue_command d->fd= %d d->released= %d\n",
|
|
||||||
d->fd,d->released);
|
|
||||||
mmc_function_spy(buf);
|
|
||||||
|
|
||||||
|
|
||||||
c->error = 0;
|
|
||||||
/*
|
|
||||||
this is valid during the mode probe in scan
|
|
||||||
if (d->fd < 1 || d->released) {
|
|
||||||
burn_print(1,
|
|
||||||
"command issued on ungrabbed drive, chaos.\n");
|
|
||||||
burn_print(1, "fd = %d, released = %d\n", d->fd,
|
|
||||||
d->released);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
memset(&s, 0, sizeof(sg_io_hdr_t));
|
|
||||||
|
|
||||||
s.interface_id = 'S';
|
|
||||||
|
|
||||||
if (c->dir == TO_DRIVE)
|
|
||||||
s.dxfer_direction = SG_DXFER_TO_DEV;
|
|
||||||
else if (c->dir == FROM_DRIVE)
|
|
||||||
s.dxfer_direction = SG_DXFER_FROM_DEV;
|
|
||||||
else if (c->dir == NO_TRANSFER) {
|
|
||||||
s.dxfer_direction = SG_DXFER_NONE;
|
|
||||||
assert(!c->page);
|
|
||||||
}
|
|
||||||
s.cmd_len = c->oplen;
|
|
||||||
s.cmdp = c->opcode;
|
|
||||||
s.mx_sb_len = 32;
|
|
||||||
s.sbp = c->sense;
|
|
||||||
memset(c->sense, 0, sizeof(c->sense));
|
|
||||||
s.timeout = 200000;
|
|
||||||
if (c->page) {
|
|
||||||
s.dxferp = c->page->data;
|
|
||||||
if (c->dir == FROM_DRIVE) {
|
|
||||||
s.dxfer_len = BUFFER_SIZE;
|
|
||||||
/* touch page so we can use valgrind */
|
|
||||||
memset(c->page->data, 0, BUFFER_SIZE);
|
|
||||||
} else {
|
|
||||||
assert(c->page->bytes > 0);
|
|
||||||
s.dxfer_len = c->page->bytes;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.dxferp = NULL;
|
|
||||||
s.dxfer_len = 0;
|
|
||||||
}
|
|
||||||
s.usr_ptr = c;
|
|
||||||
|
|
||||||
do {
|
|
||||||
err = ioctl(d->fd, SG_IO, &s);
|
|
||||||
assert(err != -1);
|
|
||||||
if (s.sb_len_wr) {
|
|
||||||
if (!c->retry) {
|
|
||||||
c->error = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
switch (scsi_error(d, s.sbp, s.sb_len_wr)) {
|
|
||||||
case RETRY:
|
|
||||||
done = 0;
|
|
||||||
break;
|
|
||||||
case FAIL:
|
|
||||||
done = 1;
|
|
||||||
c->error = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
done = 1;
|
|
||||||
}
|
|
||||||
} while (!done);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
|
||||||
int senselen)
|
|
||||||
{
|
|
||||||
int key, asc, ascq;
|
|
||||||
|
|
||||||
senselen = senselen;
|
|
||||||
key = sense[2];
|
|
||||||
asc = sense[12];
|
|
||||||
ascq = sense[13];
|
|
||||||
|
|
||||||
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
|
||||||
key, asc, ascq, d->idata->vendor, d->idata->product);
|
|
||||||
|
|
||||||
switch (asc) {
|
|
||||||
case 0:
|
|
||||||
burn_print(12, "NO ERROR!\n");
|
|
||||||
return RETRY;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
burn_print(1, "not ready\n");
|
|
||||||
return RETRY;
|
|
||||||
case 4:
|
|
||||||
burn_print(1,
|
|
||||||
"logical unit is in the process of becoming ready\n");
|
|
||||||
return RETRY;
|
|
||||||
case 0x20:
|
|
||||||
if (key == 5)
|
|
||||||
burn_print(1, "bad opcode\n");
|
|
||||||
return FAIL;
|
|
||||||
case 0x21:
|
|
||||||
burn_print(1, "invalid address or something\n");
|
|
||||||
return FAIL;
|
|
||||||
case 0x24:
|
|
||||||
if (key == 5)
|
|
||||||
burn_print(1, "invalid field in cdb\n");
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
return FAIL;
|
|
||||||
case 0x26:
|
|
||||||
if ( key == 5 )
|
|
||||||
burn_print( 1, "invalid field in parameter list\n" );
|
|
||||||
return FAIL;
|
|
||||||
case 0x28:
|
|
||||||
if (key == 6)
|
|
||||||
burn_print(1,
|
|
||||||
"Not ready to ready change, medium may have changed\n");
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
return RETRY;
|
|
||||||
case 0x3A:
|
|
||||||
burn_print(12, "Medium not present in %s %s\n",
|
|
||||||
d->idata->vendor, d->idata->product);
|
|
||||||
|
|
||||||
d->status = BURN_DISC_EMPTY;
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
burn_print(1, "unknown failure\n");
|
|
||||||
burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
25
libburn/sg.h
25
libburn/sg.h
@ -3,17 +3,32 @@
|
|||||||
#ifndef __SG
|
#ifndef __SG
|
||||||
#define __SG
|
#define __SG
|
||||||
|
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* see os.h for name of particular os-*.h where this is defined */
|
||||||
|
BURN_OS_DEFINE_DRIVE_ENUMERATOR_T
|
||||||
|
|
||||||
|
|
||||||
struct burn_drive;
|
struct burn_drive;
|
||||||
struct command;
|
struct command;
|
||||||
|
|
||||||
enum response
|
|
||||||
{ RETRY, FAIL };
|
|
||||||
|
|
||||||
void sg_enumerate(void);
|
/* ts A60922 ticket 33 */
|
||||||
void ata_enumerate(void);
|
int sg_give_next_adr(burn_drive_enumerator_t *enm_context,
|
||||||
|
char adr[], int adr_size, int initialize);
|
||||||
|
int sg_is_enumerable_adr(char *adr);
|
||||||
|
int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no,
|
||||||
|
int *target_no, int *lun_no);
|
||||||
|
|
||||||
int sg_grab(struct burn_drive *);
|
int sg_grab(struct burn_drive *);
|
||||||
int sg_release(struct burn_drive *);
|
int sg_release(struct burn_drive *);
|
||||||
int sg_issue_command(struct burn_drive *, struct command *);
|
int sg_issue_command(struct burn_drive *, struct command *);
|
||||||
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
|
||||||
|
/* ts A61115 : formerly sg_enumerate();ata_enumerate() */
|
||||||
|
int scsi_enumerate_drives(void);
|
||||||
|
|
||||||
|
int sg_drive_is_open(struct burn_drive * d);
|
||||||
|
|
||||||
#endif /* __SG */
|
#endif /* __SG */
|
||||||
|
@ -22,6 +22,10 @@ enum burn_source_status burn_track_set_source(struct burn_track *t,
|
|||||||
return BURN_SOURCE_FAILED;
|
return BURN_SOURCE_FAILED;
|
||||||
s->refcount++;
|
s->refcount++;
|
||||||
t->source = s;
|
t->source = s;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
t->open_ended = (s->get_size(s) <= 0);
|
||||||
|
|
||||||
return BURN_SOURCE_OK;
|
return BURN_SOURCE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +33,7 @@ struct burn_source *burn_source_new(void)
|
|||||||
{
|
{
|
||||||
struct burn_source *out;
|
struct burn_source *out;
|
||||||
|
|
||||||
out = malloc(sizeof(struct burn_source));
|
out = calloc(1, sizeof(struct burn_source));
|
||||||
memset(out, 0, sizeof(struct burn_source));
|
|
||||||
out->refcount = 1;
|
out->refcount = 1;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
377
libburn/spc.c
377
libburn/spc.c
@ -9,9 +9,13 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
|
/* ts A61008 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "libburn.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
#include "spc.h"
|
#include "spc.h"
|
||||||
#include "mmc.h"
|
#include "mmc.h"
|
||||||
@ -20,6 +24,10 @@
|
|||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
|
||||||
/* spc command set */
|
/* spc command set */
|
||||||
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
|
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 };
|
||||||
|
|
||||||
@ -72,7 +80,7 @@ int spc_get_erase_progress(struct burn_drive *d)
|
|||||||
void spc_inquiry(struct burn_drive *d)
|
void spc_inquiry(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct scsi_inquiry_data *id;
|
struct burn_scsi_inquiry_data *id;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
memcpy(c.opcode, SPC_INQUIRY, sizeof(SPC_INQUIRY));
|
||||||
@ -84,7 +92,7 @@ void spc_inquiry(struct burn_drive *d)
|
|||||||
c.dir = FROM_DRIVE;
|
c.dir = FROM_DRIVE;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
|
|
||||||
id = (struct scsi_inquiry_data *)d->idata;
|
id = (struct burn_scsi_inquiry_data *)d->idata;
|
||||||
id->vendor[8] = 0;
|
id->vendor[8] = 0;
|
||||||
id->product[16] = 0;
|
id->product[16] = 0;
|
||||||
id->revision[4] = 0;
|
id->revision[4] = 0;
|
||||||
@ -125,9 +133,13 @@ void spc_sense_caps(struct burn_drive *d)
|
|||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct scsi_mode_data *m;
|
struct scsi_mode_data *m;
|
||||||
int size;
|
int size, page_length, num_write_speeds = 0, i, speed, ret;
|
||||||
unsigned char *page;
|
unsigned char *page;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
struct burn_speed_descriptor *sd;
|
||||||
|
|
||||||
|
/* ts A61225 : 1 = report about post-MMC-1 speed descriptors */
|
||||||
|
static int speed_debug = 0;
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -143,6 +155,17 @@ void spc_sense_caps(struct burn_drive *d)
|
|||||||
m = d->mdata;
|
m = d->mdata;
|
||||||
page = c.page->data + 8;
|
page = c.page->data + 8;
|
||||||
|
|
||||||
|
/* ts A61225 :
|
||||||
|
Although MODE SENSE indeed belongs to SPC, the returned code page
|
||||||
|
2Ah is part of MMC-1 to MMC-3. In MMC-1 5.2.3.4. it has 22 bytes,
|
||||||
|
in MMC-3 6.3.11 there are at least 28 bytes plus a variable length
|
||||||
|
set of speed descriptors. In MMC-5 E.11 it is declared "legacy".
|
||||||
|
*/
|
||||||
|
page_length = page[1];
|
||||||
|
|
||||||
|
m->valid = 0;
|
||||||
|
burn_mdata_free_subs(m);
|
||||||
|
|
||||||
m->buffer_size = page[12] * 256 + page[13];
|
m->buffer_size = page[12] * 256 + page[13];
|
||||||
m->dvdram_read = page[2] & 32;
|
m->dvdram_read = page[2] & 32;
|
||||||
m->dvdram_write = page[3] & 32;
|
m->dvdram_write = page[3] & 32;
|
||||||
@ -154,15 +177,84 @@ void spc_sense_caps(struct burn_drive *d)
|
|||||||
m->cdrw_write = page[3] & 2;
|
m->cdrw_write = page[3] & 2;
|
||||||
m->cdr_read = page[2] & 1;
|
m->cdr_read = page[2] & 1;
|
||||||
m->cdr_write = page[3] & 1;
|
m->cdr_write = page[3] & 1;
|
||||||
|
|
||||||
|
m->c2_pointers = page[5] & 16;
|
||||||
|
m->underrun_proof = page[4] & 128;
|
||||||
|
|
||||||
|
/* ts A61021 : these fields are marked obsolete in MMC 3 */
|
||||||
m->max_read_speed = page[8] * 256 + page[9];
|
m->max_read_speed = page[8] * 256 + page[9];
|
||||||
m->cur_read_speed = page[14] * 256 + page[15];
|
m->cur_read_speed = page[14] * 256 + page[15];
|
||||||
|
|
||||||
m->max_write_speed = page[18] * 256 + page[19];
|
m->max_write_speed = page[18] * 256 + page[19];
|
||||||
m->cur_write_speed = page[20] * 256 + page[21];
|
m->cur_write_speed = page[20] * 256 + page[21];
|
||||||
m->c2_pointers = page[5] & 16;
|
|
||||||
|
/* ts A61021 : New field to be set by atip (or following MMC-3 info) */
|
||||||
|
m->min_write_speed = m->max_write_speed;
|
||||||
|
|
||||||
|
/* ts A61225 : for ACh GET PERFORMANCE, Type 03h */
|
||||||
|
m->min_end_lba = 0x7fffffff;
|
||||||
|
m->max_end_lba = 0;
|
||||||
|
|
||||||
m->valid = 1;
|
m->valid = 1;
|
||||||
m->underrun_proof = page[4] & 128;
|
|
||||||
|
mmc_get_configuration(d);
|
||||||
|
|
||||||
|
/* ts A61225 : end of MMC-1 , begin of MMC-3 */
|
||||||
|
if (page_length < 32) /* no write speed descriptors ? */
|
||||||
|
goto try_mmc_get_performance;
|
||||||
|
|
||||||
|
m->cur_write_speed = page[28] * 256 + page[29];
|
||||||
|
|
||||||
|
if (speed_debug)
|
||||||
|
fprintf(stderr, "LIBBURN_DEBUG: cur_write_speed = %d\n",
|
||||||
|
m->cur_write_speed);
|
||||||
|
|
||||||
|
num_write_speeds = page[30] * 256 + page[31];
|
||||||
|
m->max_write_speed = m->min_write_speed = m->cur_write_speed;
|
||||||
|
for (i = 0; i < num_write_speeds; i++) {
|
||||||
|
speed = page[32 + 4*i + 2] * 256 + page[32 + 4*i + 3];
|
||||||
|
|
||||||
|
if (speed_debug)
|
||||||
|
fprintf(stderr,
|
||||||
|
"LIBBURN_DEBUG: write speed #%d = %d kB/s (rc %d)\n",
|
||||||
|
i, speed, page[32 + 4*i +1] & 7);
|
||||||
|
|
||||||
|
/* ts A61226 */
|
||||||
|
ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors),
|
||||||
|
NULL, d->mdata->speed_descriptors, 0);
|
||||||
|
if (ret > 0) {
|
||||||
|
sd = d->mdata->speed_descriptors;
|
||||||
|
sd->source = 1;
|
||||||
|
if (d->current_profile > 0) {
|
||||||
|
sd->profile_loaded = d->current_profile;
|
||||||
|
strcpy(sd->profile_name,
|
||||||
|
d->current_profile_text);
|
||||||
|
}
|
||||||
|
sd->wrc = (( page[32 + 4*i +1] & 7 ) == 1 );
|
||||||
|
sd->write_speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speed > m->max_write_speed)
|
||||||
|
m->max_write_speed = speed;
|
||||||
|
if (speed < m->min_write_speed)
|
||||||
|
m->min_write_speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speed_debug)
|
||||||
|
fprintf(stderr,
|
||||||
|
"LIBBURN_DEBUG: 5Ah,2Ah min_write_speed = %d , max_write_speed = %d\n",
|
||||||
|
m->min_write_speed, m->max_write_speed);
|
||||||
|
|
||||||
|
try_mmc_get_performance:;
|
||||||
|
ret = mmc_get_write_performance(d);
|
||||||
|
|
||||||
|
if (ret > 0 && speed_debug)
|
||||||
|
fprintf(stderr,
|
||||||
|
"LIBBURN_DEBUG: ACh min_write_speed = %d , max_write_speed = %d\n",
|
||||||
|
m->min_write_speed, m->max_write_speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void spc_sense_error_params(struct burn_drive *d)
|
void spc_sense_error_params(struct burn_drive *d)
|
||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
@ -202,7 +294,10 @@ void spc_select_error_params(struct burn_drive *d,
|
|||||||
c.page = &buf;
|
c.page = &buf;
|
||||||
c.page->bytes = 0;
|
c.page->bytes = 0;
|
||||||
c.page->sectors = 0;
|
c.page->sectors = 0;
|
||||||
assert(d->mdata->valid);
|
|
||||||
|
/* ts A61007 : moved up to only caller burn_disc_read() */
|
||||||
|
/* a ssert(d->mdata->valid); */
|
||||||
|
|
||||||
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
memset(c.page->data, 0, 8 + 2 + d->mdata->retry_page_length);
|
||||||
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
c.page->bytes = 8 + 2 + d->mdata->retry_page_length;
|
||||||
c.page->data[8] = 1;
|
c.page->data[8] = 1;
|
||||||
@ -227,8 +322,9 @@ void spc_sense_write_params(struct burn_drive *d)
|
|||||||
unsigned char *page;
|
unsigned char *page;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
|
||||||
assert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
/* ts A61007 : Done in soft at only caller burn_drive_grab() */
|
||||||
d->mdata->dvdr_write || d->mdata->dvdram_write);
|
/* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write ||
|
||||||
|
d->mdata->dvdr_write || d->mdata->dvdram_write); */
|
||||||
|
|
||||||
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
memcpy(c.opcode, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
@ -249,14 +345,35 @@ void spc_sense_write_params(struct burn_drive *d)
|
|||||||
mmc_read_disc_info(d);
|
mmc_read_disc_info(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61229 */
|
||||||
|
#define Libburn_mmc_compose_mode_page_5 1
|
||||||
|
|
||||||
|
|
||||||
|
/* remark ts A61104 :
|
||||||
|
Although command MODE SELECT is SPC, the content of the
|
||||||
|
Write Parameters Mode Page (05h) is MMC (Table 108 in MMC-1).
|
||||||
|
Thus the filling of the mode page should be done by a mmc_ function.
|
||||||
|
*/
|
||||||
void spc_select_write_params(struct burn_drive *d,
|
void spc_select_write_params(struct burn_drive *d,
|
||||||
const struct burn_write_opts *o)
|
const struct burn_write_opts *o)
|
||||||
{
|
{
|
||||||
struct buffer buf;
|
struct buffer buf;
|
||||||
struct command c;
|
struct command c;
|
||||||
|
#ifndef Libburn_mmc_compose_mode_page_5
|
||||||
int bufe, sim;
|
int bufe, sim;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ts A61007 : All current callers are safe. */
|
||||||
|
/* a ssert(o->drive == d); */
|
||||||
|
|
||||||
|
/* <<< A61030
|
||||||
|
fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n",
|
||||||
|
o->write_type,o->multi,o->control);
|
||||||
|
fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n",
|
||||||
|
o->block_type,spc_block_type(o->block_type));
|
||||||
|
*/
|
||||||
|
|
||||||
assert(o->drive == d);
|
|
||||||
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
memcpy(c.opcode, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
|
||||||
c.retry = 1;
|
c.retry = 1;
|
||||||
c.oplen = sizeof(SPC_MODE_SELECT);
|
c.oplen = sizeof(SPC_MODE_SELECT);
|
||||||
@ -264,24 +381,50 @@ void spc_select_write_params(struct burn_drive *d,
|
|||||||
c.page = &buf;
|
c.page = &buf;
|
||||||
c.page->bytes = 0;
|
c.page->bytes = 0;
|
||||||
c.page->sectors = 0;
|
c.page->sectors = 0;
|
||||||
assert(d->mdata->valid);
|
|
||||||
|
/* ts A61007 : moved up to burn_disc_write() */
|
||||||
|
/* a ssert(d->mdata->valid); */
|
||||||
|
|
||||||
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
memset(c.page->data, 0, 8 + 2 + d->mdata->write_page_length);
|
||||||
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
c.page->bytes = 8 + 2 + d->mdata->write_page_length;
|
||||||
c.page->data[8] = 5;
|
|
||||||
c.page->data[9] = d->mdata->write_page_length;
|
|
||||||
|
|
||||||
burn_print(12, "using write page length %d (valid %d)\n",
|
burn_print(12, "using write page length %d (valid %d)\n",
|
||||||
d->mdata->write_page_length, d->mdata->write_page_valid);
|
d->mdata->write_page_length, d->mdata->write_page_valid);
|
||||||
|
|
||||||
|
#ifdef Libburn_mmc_compose_mode_page_5
|
||||||
|
|
||||||
|
/* ts A61229 */
|
||||||
|
if (mmc_compose_mode_page_5(d, o, c.page->data + 8) <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
c.page->data[8] = 5;
|
||||||
|
c.page->data[9] = d->mdata->write_page_length;
|
||||||
|
|
||||||
bufe = o->underrun_proof;
|
bufe = o->underrun_proof;
|
||||||
sim = o->simulate;
|
sim = o->simulate;
|
||||||
c.page->data[10] = (bufe << 6)
|
c.page->data[10] = (bufe << 6)
|
||||||
+ (sim << 4)
|
+ (sim << 4)
|
||||||
+ o->write_type;
|
+ o->write_type;
|
||||||
c.page->data[11] = (o->multi << 6) | o->control;
|
|
||||||
|
/* ts A61106 : MMC-1 table 110 : multi==0 or multi==3 */
|
||||||
|
c.page->data[11] = ((3 * !!o->multi) << 6) | o->control;
|
||||||
|
|
||||||
c.page->data[12] = spc_block_type(o->block_type);
|
c.page->data[12] = spc_block_type(o->block_type);
|
||||||
|
|
||||||
|
/* ts A61104 */
|
||||||
|
if(!(o->control&4)) /* audio (MMC-1 table 61) */
|
||||||
|
if(o->write_type == BURN_WRITE_TAO) /* ??? for others too ? */
|
||||||
|
c.page->data[12] = 0; /* Data Block Type: Raw Data */
|
||||||
|
|
||||||
c.page->data[22] = 0;
|
c.page->data[22] = 0;
|
||||||
c.page->data[23] = 150; /* audio pause length */
|
c.page->data[23] = 150; /* audio pause length */
|
||||||
|
|
||||||
/*XXX need session format! */
|
/*XXX need session format! */
|
||||||
|
|
||||||
|
#endif /* ! Libburn_mmc_compose_mode_page_5 */
|
||||||
|
|
||||||
c.dir = TO_DRIVE;
|
c.dir = TO_DRIVE;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
}
|
}
|
||||||
@ -291,6 +434,10 @@ void spc_getcaps(struct burn_drive *d)
|
|||||||
spc_inquiry(d);
|
spc_inquiry(d);
|
||||||
spc_sense_caps(d);
|
spc_sense_caps(d);
|
||||||
spc_sense_error_params(d);
|
spc_sense_error_params(d);
|
||||||
|
|
||||||
|
/* <<< for debugging. >>> ??? to be fixely included here ?
|
||||||
|
mmc_read_format_capacities(d, -1);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -331,7 +478,10 @@ void spc_probe_write_modes(struct burn_drive *d)
|
|||||||
c.page->data[12] = try_block_type;
|
c.page->data[12] = try_block_type;
|
||||||
c.page->data[23] = 150;
|
c.page->data[23] = 150;
|
||||||
c.dir = TO_DRIVE;
|
c.dir = TO_DRIVE;
|
||||||
|
|
||||||
|
d->silent_on_scsi_error = 1;
|
||||||
d->issue_command(d, &c);
|
d->issue_command(d, &c);
|
||||||
|
d->silent_on_scsi_error = 0;
|
||||||
|
|
||||||
key = c.sense[2];
|
key = c.sense[2];
|
||||||
asc = c.sense[12];
|
asc = c.sense[12];
|
||||||
@ -375,6 +525,9 @@ void spc_probe_write_modes(struct burn_drive *d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ( ts A61229 : shouldn't this go to mmc.c too ?) */
|
||||||
|
|
||||||
|
/** @return -1 = error */
|
||||||
int spc_block_type(enum burn_block_types b)
|
int spc_block_type(enum burn_block_types b)
|
||||||
{
|
{
|
||||||
switch (b) {
|
switch (b) {
|
||||||
@ -400,6 +553,200 @@ int spc_block_type(enum burn_block_types b)
|
|||||||
return 12;
|
return 12;
|
||||||
case BURN_BLOCK_MODE2_OK:
|
case BURN_BLOCK_MODE2_OK:
|
||||||
return 13;
|
return 13;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
assert(0);
|
/* ts A61007 : already prevented in burn_write_opts_set_write_type() */
|
||||||
|
/* a ssert(0); */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ts A61021 : the spc specific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int spc_setup_drive(struct burn_drive *d)
|
||||||
|
{
|
||||||
|
d->getcaps = spc_getcaps;
|
||||||
|
d->lock = spc_prevent;
|
||||||
|
d->unlock = spc_allow;
|
||||||
|
d->read_disc_info = spc_sense_write_params;
|
||||||
|
d->get_erase_progress = spc_get_erase_progress;
|
||||||
|
d->test_unit_ready = spc_test_unit_ready;
|
||||||
|
d->probe_write_modes = spc_probe_write_modes;
|
||||||
|
d->send_parameters = spc_select_error_params;
|
||||||
|
d->send_write_parameters = spc_select_write_params;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common()
|
||||||
|
@param flag Bitfiled for control purposes
|
||||||
|
bit0= do not setup spc/sbc/mmc
|
||||||
|
*/
|
||||||
|
int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no, int flag)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* ts A60923 */
|
||||||
|
d->bus_no = bus_no;
|
||||||
|
d->host = host_no;
|
||||||
|
d->id = target_no;
|
||||||
|
d->channel = channel_no;
|
||||||
|
d->lun = lun_no;
|
||||||
|
|
||||||
|
/* ts A61106 */
|
||||||
|
d->silent_on_scsi_error = 0;
|
||||||
|
|
||||||
|
|
||||||
|
d->idata = malloc(sizeof(struct burn_scsi_inquiry_data));
|
||||||
|
d->idata->valid = 0;
|
||||||
|
d->mdata = malloc(sizeof(struct scsi_mode_data));
|
||||||
|
d->mdata->valid = 0;
|
||||||
|
d->mdata->speed_descriptors = NULL;
|
||||||
|
|
||||||
|
/* ts A61007 : obsolete Assert in drive_getcaps() */
|
||||||
|
if(d->idata == NULL || d->mdata == NULL) {
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020108,
|
||||||
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Could not allocate new drive object", 0, 0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!(flag & 1)) {
|
||||||
|
ret = spc_setup_drive(d);
|
||||||
|
if (ret<=0)
|
||||||
|
return ret;
|
||||||
|
ret = sbc_setup_drive(d);
|
||||||
|
if (ret<=0)
|
||||||
|
return ret;
|
||||||
|
ret = mmc_setup_drive(d);
|
||||||
|
if (ret<=0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61122 */
|
||||||
|
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
|
||||||
|
int senselen, char msg[161],
|
||||||
|
int *key, int *asc, int *ascq)
|
||||||
|
{
|
||||||
|
*key= *asc= *ascq= -1;
|
||||||
|
|
||||||
|
if (senselen<=0 || senselen>2)
|
||||||
|
*key = sense[2];
|
||||||
|
if (senselen<=0 || senselen>12)
|
||||||
|
*asc = sense[12];
|
||||||
|
if (senselen<=0 || senselen>13)
|
||||||
|
*ascq = sense[13];
|
||||||
|
|
||||||
|
burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n",
|
||||||
|
*key, *asc, *ascq, d->idata->vendor, d->idata->product);
|
||||||
|
|
||||||
|
switch (*asc) {
|
||||||
|
case 0:
|
||||||
|
sprintf(msg, "(no error reported by SCSI transaction)");
|
||||||
|
return RETRY;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
sprintf(msg, "not ready");
|
||||||
|
return RETRY;
|
||||||
|
case 4:
|
||||||
|
sprintf(msg,
|
||||||
|
"logical unit is in the process of becoming ready");
|
||||||
|
return RETRY;
|
||||||
|
case 0x20:
|
||||||
|
if (*key == 5)
|
||||||
|
sprintf(msg, "bad opcode");
|
||||||
|
return FAIL;
|
||||||
|
case 0x21:
|
||||||
|
sprintf(msg, "invalid address");
|
||||||
|
return FAIL;
|
||||||
|
case 0x24:
|
||||||
|
if (*key == 5)
|
||||||
|
sprintf(msg, "invalid field in cdb");
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
return FAIL;
|
||||||
|
case 0x26:
|
||||||
|
if (*key == 5 )
|
||||||
|
sprintf(msg, "invalid field in parameter list" );
|
||||||
|
return FAIL;
|
||||||
|
case 0x28:
|
||||||
|
if (*key == 6)
|
||||||
|
sprintf(msg, "Medium may have changed");
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
return RETRY;
|
||||||
|
case 0x3A:
|
||||||
|
sprintf(msg, "Medium not present");
|
||||||
|
d->status = BURN_DISC_EMPTY;
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
sprintf(msg,
|
||||||
|
"Failure. See mmc3r10g.pdf: Sense Key %X ASC %2.2X ASCQ %2.2X",
|
||||||
|
*key, *asc, *ascq);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61115 moved from sg-*.c */
|
||||||
|
/* ts A61122 made it frontend to scsi_error_msg() */
|
||||||
|
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
|
||||||
|
int senselen)
|
||||||
|
{
|
||||||
|
int key, asc, ascq;
|
||||||
|
char msg[160];
|
||||||
|
enum response resp;
|
||||||
|
|
||||||
|
resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq);
|
||||||
|
if (asc == 0 || asc == 0x3A)
|
||||||
|
burn_print(12, "%s\n", msg);
|
||||||
|
else
|
||||||
|
burn_print(1, "%s\n", msg);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61030 - A61115 */
|
||||||
|
/* @param flag bit0=do report conditions which are considered not an error */
|
||||||
|
int scsi_notify_error(struct burn_drive *d, struct command *c,
|
||||||
|
unsigned char *sense, int senselen, int flag)
|
||||||
|
{
|
||||||
|
int key= -1, asc= -1, ascq= -1, ret;
|
||||||
|
char msg[320],scsi_msg[160];
|
||||||
|
|
||||||
|
if (d->silent_on_scsi_error)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
strcpy(scsi_msg, " \"");
|
||||||
|
scsi_error_msg(d, sense, senselen, scsi_msg + strlen(scsi_msg),
|
||||||
|
&key, &asc, &ascq);
|
||||||
|
strcat(scsi_msg, "\"");
|
||||||
|
|
||||||
|
if(!(flag & 1)) {
|
||||||
|
/* SPC : TEST UNIT READY command */
|
||||||
|
if (c->opcode[0] == 0)
|
||||||
|
return 1;
|
||||||
|
/* MMC : READ DISC INFORMATION command */
|
||||||
|
if (c->opcode[0] == 0x51)
|
||||||
|
if (key == 0x2 && asc == 0x3A &&
|
||||||
|
ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]);
|
||||||
|
if (key>=0)
|
||||||
|
sprintf(msg+strlen(msg), " key=%Xh", key);
|
||||||
|
if (asc>=0)
|
||||||
|
sprintf(msg+strlen(msg), " asc=%2.2Xh", asc);
|
||||||
|
if (ascq>=0)
|
||||||
|
sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq);
|
||||||
|
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
|
||||||
|
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
|
||||||
|
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
scsi_msg,0,0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -22,4 +22,29 @@ int spc_block_type(enum burn_block_types b);
|
|||||||
int spc_get_erase_progress(struct burn_drive *d);
|
int spc_get_erase_progress(struct burn_drive *d);
|
||||||
int spc_test_unit_ready(struct burn_drive *d);
|
int spc_test_unit_ready(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61021 : the spc specific part of sg.c:enumerate_common()
|
||||||
|
*/
|
||||||
|
int spc_setup_drive(struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common()
|
||||||
|
@param flag Bitfield for control purposes
|
||||||
|
bit0= do not setup spc/sbc/mmc
|
||||||
|
*/
|
||||||
|
int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
|
||||||
|
int channel_no, int target_no, int lun_no, int flag);
|
||||||
|
|
||||||
|
/* ts A61115 moved from sg-*.h */
|
||||||
|
enum response { RETRY, FAIL };
|
||||||
|
enum response scsi_error(struct burn_drive *, unsigned char *, int);
|
||||||
|
|
||||||
|
/* ts A61122 */
|
||||||
|
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
|
||||||
|
int senselen, char msg[161],
|
||||||
|
int *key, int *asc, int *ascq);
|
||||||
|
|
||||||
|
/* ts A61030 */
|
||||||
|
/* @param flag bit0=do report conditions which are considered not an error */
|
||||||
|
int scsi_notify_error(struct burn_drive *, struct command *c,
|
||||||
|
unsigned char *sense, int senselen, int flag);
|
||||||
|
|
||||||
#endif /*__SPC*/
|
#endif /*__SPC*/
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#include <assert.h>
|
|
||||||
|
/* ts A61008 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -7,10 +10,18 @@
|
|||||||
#include "write.h"
|
#include "write.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include "libdax_msgs.h"
|
||||||
|
extern struct libdax_msgs *libdax_messenger;
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61008 : replaced Assert by if and return 0 */
|
||||||
|
/* a ssert(!(pos > BURN_POS_END)); */
|
||||||
|
|
||||||
#define RESIZE(TO, NEW, pos) {\
|
#define RESIZE(TO, NEW, pos) {\
|
||||||
void *tmp;\
|
void *tmp;\
|
||||||
\
|
\
|
||||||
assert(!(pos > BURN_POS_END));\
|
if (pos > BURN_POS_END)\
|
||||||
|
return 0;\
|
||||||
if (pos == BURN_POS_END)\
|
if (pos == BURN_POS_END)\
|
||||||
pos = TO->NEW##s;\
|
pos = TO->NEW##s;\
|
||||||
if (pos > TO->NEW##s)\
|
if (pos > TO->NEW##s)\
|
||||||
@ -28,8 +39,7 @@
|
|||||||
struct burn_disc *burn_disc_create(void)
|
struct burn_disc *burn_disc_create(void)
|
||||||
{
|
{
|
||||||
struct burn_disc *d;
|
struct burn_disc *d;
|
||||||
d = malloc(sizeof(struct burn_disc));
|
d = calloc(1, sizeof(struct burn_disc));
|
||||||
memset(d, 0, sizeof(struct burn_disc));
|
|
||||||
d->refcnt = 1;
|
d->refcnt = 1;
|
||||||
d->sessions = 0;
|
d->sessions = 0;
|
||||||
d->session = NULL;
|
d->session = NULL;
|
||||||
@ -53,8 +63,7 @@ void burn_disc_free(struct burn_disc *d)
|
|||||||
struct burn_session *burn_session_create(void)
|
struct burn_session *burn_session_create(void)
|
||||||
{
|
{
|
||||||
struct burn_session *s;
|
struct burn_session *s;
|
||||||
s = malloc(sizeof(struct burn_session));
|
s = calloc(1, sizeof(struct burn_session));
|
||||||
memset(s, 0, sizeof(struct burn_session));
|
|
||||||
s->refcnt = 1;
|
s->refcnt = 1;
|
||||||
s->tracks = 0;
|
s->tracks = 0;
|
||||||
s->track = NULL;
|
s->track = NULL;
|
||||||
@ -94,8 +103,7 @@ int burn_disc_add_session(struct burn_disc *d, struct burn_session *s,
|
|||||||
struct burn_track *burn_track_create(void)
|
struct burn_track *burn_track_create(void)
|
||||||
{
|
{
|
||||||
struct burn_track *t;
|
struct burn_track *t;
|
||||||
t = malloc(sizeof(struct burn_track));
|
t = calloc(1, sizeof(struct burn_track));
|
||||||
memset(t, 0, sizeof(struct burn_track));
|
|
||||||
t->refcnt = 1;
|
t->refcnt = 1;
|
||||||
t->indices = 0;
|
t->indices = 0;
|
||||||
t->offset = 0;
|
t->offset = 0;
|
||||||
@ -107,9 +115,23 @@ struct burn_track *burn_track_create(void)
|
|||||||
t->pad = 1;
|
t->pad = 1;
|
||||||
t->entry = NULL;
|
t->entry = NULL;
|
||||||
t->source = NULL;
|
t->source = NULL;
|
||||||
|
t->eos = 0;
|
||||||
|
|
||||||
|
/* ts A61101 */
|
||||||
|
t->sourcecount = 0;
|
||||||
|
t->writecount = 0;
|
||||||
|
t->written_sectors = 0;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
t->open_ended = 0;
|
||||||
|
t->track_data_done = 0;
|
||||||
|
|
||||||
t->postgap = 0;
|
t->postgap = 0;
|
||||||
t->pregap1 = 0;
|
t->pregap1 = 0;
|
||||||
t->pregap2 = 0;
|
t->pregap2 = 0;
|
||||||
|
|
||||||
|
/* ts A61024 */
|
||||||
|
t->swap_source_bytes = 0;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,30 +160,34 @@ int burn_session_remove_track(struct burn_session *s, struct burn_track *t)
|
|||||||
struct burn_track **tmp;
|
struct burn_track **tmp;
|
||||||
int i, pos = -1;
|
int i, pos = -1;
|
||||||
|
|
||||||
assert(s->track != NULL);
|
/* ts A61008 */
|
||||||
|
/* a ssert(s->track != NULL); */
|
||||||
|
if (s->track == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
burn_track_free(t);
|
burn_track_free(t);
|
||||||
|
|
||||||
/* Find the position */
|
/* Find the position */
|
||||||
for (i = 0; i < s->tracks; i++) {
|
for (i = 0; i < s->tracks; i++) {
|
||||||
if (t == s->track[i])
|
if (t == s->track[i]) {
|
||||||
pos = i;
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Is it the last track? */
|
/* Is it the last track? */
|
||||||
if (pos != s->tracks) {
|
if (pos != s->tracks - 1) {
|
||||||
memmove(s->track[pos], s->track[pos + 1],
|
memmove(&s->track[pos], &s->track[pos + 1],
|
||||||
sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
|
sizeof(struct burn_track *) * (s->tracks - (pos + 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
s->tracks--;
|
s->tracks--;
|
||||||
tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
|
tmp = realloc(s->track, sizeof(struct burn_track *) * s->tracks);
|
||||||
if (!tmp)
|
if (tmp)
|
||||||
return 0;
|
s->track = tmp;
|
||||||
s->track = tmp;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,34 +218,96 @@ void burn_structure_print_track(struct burn_track *t)
|
|||||||
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
void burn_track_define_data(struct burn_track *t, int offset, int tail,
|
||||||
int pad, int mode)
|
int pad, int mode)
|
||||||
{
|
{
|
||||||
|
int type_to_form(int mode, unsigned char *ctladr, int *form);
|
||||||
|
int burn_sector_length(int tracktype);
|
||||||
|
unsigned char ctladr;
|
||||||
|
int form = -1; /* unchanged form will be considered an error too */
|
||||||
|
|
||||||
|
type_to_form(mode, &ctladr, &form);
|
||||||
|
if (form == -1 || burn_sector_length(mode) <= 0) {
|
||||||
|
char msg[160];
|
||||||
|
|
||||||
|
sprintf(msg, "Attempt to set track mode to unusable value %d",
|
||||||
|
mode);
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020115,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
msg, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
t->offset = offset;
|
t->offset = offset;
|
||||||
t->pad = pad;
|
t->pad = pad;
|
||||||
t->mode = mode;
|
t->mode = mode;
|
||||||
t->tail = tail;
|
t->tail = tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61024 */
|
||||||
|
int burn_track_set_byte_swap(struct burn_track *t, int swap_source_bytes)
|
||||||
|
{
|
||||||
|
if(swap_source_bytes!=0 && swap_source_bytes!=1)
|
||||||
|
return 0;
|
||||||
|
t->swap_source_bytes = swap_source_bytes;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
void burn_track_set_isrc(struct burn_track *t, char *country, char *owner,
|
||||||
unsigned char year, unsigned int serial)
|
unsigned char year, unsigned int serial)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
t->isrc.has_isrc = 1;
|
|
||||||
for (i = 0; i < 2; ++i) {
|
for (i = 0; i < 2; ++i) {
|
||||||
assert((country[i] >= '0' || country[i] < '9') &&
|
|
||||||
|
/* ts A61008 : This is always true */
|
||||||
|
/* a ssert((country[i] >= '0' || country[i] < '9') &&
|
||||||
(country[i] >= 'a' || country[i] < 'z') &&
|
(country[i] >= 'a' || country[i] < 'z') &&
|
||||||
(country[i] >= 'A' || country[i] < 'Z'));
|
(country[i] >= 'A' || country[i] < 'Z')); */
|
||||||
|
/* ts A61008 : now coordinated with sector.c: char_to_isrc() */
|
||||||
|
if (! ((country[i] >= '0' && country[i] <= '9') ||
|
||||||
|
(country[i] >= 'a' && country[i] <= 'z') ||
|
||||||
|
(country[i] >= 'A' && country[i] <= 'Z') ) )
|
||||||
|
goto is_not_allowed;
|
||||||
|
|
||||||
t->isrc.country[i] = country[i];
|
t->isrc.country[i] = country[i];
|
||||||
}
|
}
|
||||||
for (i = 0; i < 3; ++i) {
|
for (i = 0; i < 3; ++i) {
|
||||||
assert((owner[i] >= '0' || owner[i] < '9') &&
|
|
||||||
|
/* ts A61008 : This is always true */
|
||||||
|
/* a ssert((owner[i] >= '0' || owner[i] < '9') &&
|
||||||
(owner[i] >= 'a' || owner[i] < 'z') &&
|
(owner[i] >= 'a' || owner[i] < 'z') &&
|
||||||
(owner[i] >= 'A' || owner[i] < 'Z'));
|
(owner[i] >= 'A' || owner[i] < 'Z')); */
|
||||||
|
/* ts A61008 : now coordinated with sector.c: char_to_isrc() */
|
||||||
|
if (! ((owner[i] >= '0' && owner[i] <= '9') ||
|
||||||
|
(owner[i] >= 'a' && owner[i] <= 'z') ||
|
||||||
|
(owner[i] >= 'A' && owner[i] <= 'Z') ) )
|
||||||
|
goto is_not_allowed;
|
||||||
|
|
||||||
t->isrc.owner[i] = owner[i];
|
t->isrc.owner[i] = owner[i];
|
||||||
}
|
}
|
||||||
assert(year <= 99);
|
|
||||||
|
/* ts A61008 */
|
||||||
|
/* a ssert(year <= 99); */
|
||||||
|
if (year > 99)
|
||||||
|
goto is_not_allowed;
|
||||||
|
|
||||||
t->isrc.year = year;
|
t->isrc.year = year;
|
||||||
assert(serial <= 99999);
|
|
||||||
|
/* ts A61008 */
|
||||||
|
/* a ssert(serial <= 99999); */
|
||||||
|
if (serial > 99999)
|
||||||
|
goto is_not_allowed;
|
||||||
|
|
||||||
t->isrc.serial = serial;
|
t->isrc.serial = serial;
|
||||||
|
|
||||||
|
/* ts A61008 */
|
||||||
|
t->isrc.has_isrc = 1;
|
||||||
|
return;
|
||||||
|
is_not_allowed:;
|
||||||
|
libdax_msgs_submit(libdax_messenger, -1, 0x00020114,
|
||||||
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
||||||
|
"Attempt to set ISRC with bad data", 0, 0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_track_clear_isrc(struct burn_track *t)
|
void burn_track_clear_isrc(struct burn_track *t)
|
||||||
@ -229,7 +317,8 @@ void burn_track_clear_isrc(struct burn_track *t)
|
|||||||
|
|
||||||
int burn_track_get_sectors(struct burn_track *t)
|
int burn_track_get_sectors(struct burn_track *t)
|
||||||
{
|
{
|
||||||
int size;
|
/* ts A70125 : was int */
|
||||||
|
off_t size;
|
||||||
int sectors, seclen;
|
int sectors, seclen;
|
||||||
|
|
||||||
seclen = burn_sector_length(t->mode);
|
seclen = burn_sector_length(t->mode);
|
||||||
@ -241,6 +330,47 @@ int burn_track_get_sectors(struct burn_track *t)
|
|||||||
return sectors;
|
return sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70125 */
|
||||||
|
int burn_track_set_sectors(struct burn_track *t, int sectors)
|
||||||
|
{
|
||||||
|
off_t size, seclen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
seclen = burn_sector_length(t->mode);
|
||||||
|
size = seclen * (off_t) sectors - (off_t) t->offset - (off_t) t->tail;
|
||||||
|
if (size < 0)
|
||||||
|
return 0;
|
||||||
|
ret = t->source->set_size(t->source, size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
int burn_track_is_open_ended(struct burn_track *t)
|
||||||
|
{
|
||||||
|
return !!t->open_ended;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A61101 : API function */
|
||||||
|
int burn_track_get_counters(struct burn_track *t,
|
||||||
|
off_t *read_bytes, off_t *written_bytes)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
fprintf(stderr, "libburn_experimental: sizeof(off_t)=%d\n",
|
||||||
|
sizeof(off_t));
|
||||||
|
*/
|
||||||
|
*read_bytes = t->sourcecount;
|
||||||
|
*written_bytes = t->writecount;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
int burn_track_is_data_done(struct burn_track *t)
|
||||||
|
{
|
||||||
|
return !!t->track_data_done;
|
||||||
|
}
|
||||||
|
|
||||||
int burn_track_get_shortage(struct burn_track *t)
|
int burn_track_get_shortage(struct burn_track *t)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
@ -273,13 +403,19 @@ int burn_disc_get_sectors(struct burn_disc *d)
|
|||||||
|
|
||||||
void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry)
|
void burn_track_get_entry(struct burn_track *t, struct burn_toc_entry *entry)
|
||||||
{
|
{
|
||||||
memcpy(entry, t->entry, sizeof(struct burn_toc_entry));
|
if (t->entry == NULL)
|
||||||
|
memset(entry, 0, sizeof(struct burn_toc_entry));
|
||||||
|
else
|
||||||
|
memcpy(entry, t->entry, sizeof(struct burn_toc_entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void burn_session_get_leadout_entry(struct burn_session *s,
|
void burn_session_get_leadout_entry(struct burn_session *s,
|
||||||
struct burn_toc_entry *entry)
|
struct burn_toc_entry *entry)
|
||||||
{
|
{
|
||||||
memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry));
|
if (s->leadout_entry == NULL)
|
||||||
|
memset(entry, 0, sizeof(struct burn_toc_entry));
|
||||||
|
else
|
||||||
|
memcpy(entry, s->leadout_entry, sizeof(struct burn_toc_entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num)
|
struct burn_session **burn_disc_get_sessions(struct burn_disc *d, int *num)
|
||||||
|
@ -31,6 +31,18 @@ struct burn_track
|
|||||||
struct burn_source *source;
|
struct burn_source *source;
|
||||||
/** End of Source flag */
|
/** End of Source flag */
|
||||||
int eos;
|
int eos;
|
||||||
|
|
||||||
|
/* ts A61101 */
|
||||||
|
off_t sourcecount;
|
||||||
|
off_t writecount;
|
||||||
|
off_t written_sectors;
|
||||||
|
|
||||||
|
/* ts A61031 */
|
||||||
|
/** Source is of undefined length */
|
||||||
|
int open_ended;
|
||||||
|
/** End of open ended track flag : offset+payload+tail are delivered */
|
||||||
|
int track_data_done;
|
||||||
|
|
||||||
/** The audio/data mode for the entry. Derived from control and
|
/** The audio/data mode for the entry. Derived from control and
|
||||||
possibly from reading the track's first sector. */
|
possibly from reading the track's first sector. */
|
||||||
int mode;
|
int mode;
|
||||||
@ -41,6 +53,10 @@ struct burn_track
|
|||||||
/** The track contains a postgap */
|
/** The track contains a postgap */
|
||||||
int postgap;
|
int postgap;
|
||||||
struct isrc isrc;
|
struct isrc isrc;
|
||||||
|
|
||||||
|
/* ts A61024 */
|
||||||
|
/** Byte swapping on source data stream : 0=none , 1=pairwise */
|
||||||
|
int swap_source_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct burn_session
|
struct burn_session
|
||||||
@ -67,4 +83,13 @@ struct burn_disc
|
|||||||
|
|
||||||
int burn_track_get_shortage(struct burn_track *t);
|
int burn_track_get_shortage(struct burn_track *t);
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A61031 : might go to libburn.h */
|
||||||
|
int burn_track_is_open_ended(struct burn_track *t);
|
||||||
|
int burn_track_is_data_done(struct burn_track *t);
|
||||||
|
|
||||||
|
/* ts A70125 */
|
||||||
|
int burn_track_set_sectors(struct burn_track *t, int sectors);
|
||||||
|
|
||||||
|
|
||||||
#endif /* BURN__STRUCTURE_H */
|
#endif /* BURN__STRUCTURE_H */
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
||||||
|
|
||||||
#include <assert.h>
|
/* ts A61008 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -51,7 +53,10 @@ static void write_clonecd2(volatile struct toc *toc, int f)
|
|||||||
case BURN_MODE2_FORM1:
|
case BURN_MODE2_FORM1:
|
||||||
case BURN_MODE2_FORM2:
|
case BURN_MODE2_FORM2:
|
||||||
case BURN_MODE_UNINITIALIZED:
|
case BURN_MODE_UNINITIALIZED:
|
||||||
assert(0); /* unhandled! find out ccd's
|
|
||||||
|
/* ts A61008 : do this softly without Assert */
|
||||||
|
|
||||||
|
a ssert(0); /* unhandled! find out ccd's
|
||||||
value for these modes! */
|
value for these modes! */
|
||||||
}
|
}
|
||||||
dprintf(f, "PreGapMode=%d\r\n", m);
|
dprintf(f, "PreGapMode=%d\r\n", m);
|
||||||
@ -95,7 +100,8 @@ void toc_find_modes(struct burn_drive *d)
|
|||||||
struct buffer mem;
|
struct buffer mem;
|
||||||
struct burn_toc_entry *e;
|
struct burn_toc_entry *e;
|
||||||
|
|
||||||
assert(d->busy);
|
/* ts A61008 : to be prevented on the higher levels */
|
||||||
|
/* a ssert(d->busy); */
|
||||||
|
|
||||||
mem.bytes = 0;
|
mem.bytes = 0;
|
||||||
mem.sectors = 1;
|
mem.sectors = 1;
|
||||||
|
@ -4,19 +4,16 @@
|
|||||||
#define __TRANSPORT
|
#define __TRANSPORT
|
||||||
|
|
||||||
#include "libburn.h"
|
#include "libburn.h"
|
||||||
|
#include "os.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
/* sg data structures */
|
/* sg data structures */
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <scsi/sg.h>
|
|
||||||
#include <scsi/scsi.h>
|
|
||||||
|
|
||||||
/* kludge! glibc headers don't define all the SCSI shit that we use! */
|
|
||||||
#ifndef SG_GET_ACCESS_COUNT
|
|
||||||
# define SG_GET_ACCESS_COUNT 0x2289
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BUFFER_SIZE 65536
|
/* see os.h for name of particular os-*.h where this is defined */
|
||||||
|
#define BUFFER_SIZE BURN_OS_TRANSPORT_BUFFER_SIZE
|
||||||
|
|
||||||
|
|
||||||
enum transfer_direction
|
enum transfer_direction
|
||||||
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
{ TO_DRIVE, FROM_DRIVE, NO_TRANSFER };
|
||||||
@ -39,7 +36,12 @@ struct params
|
|||||||
|
|
||||||
struct buffer
|
struct buffer
|
||||||
{
|
{
|
||||||
unsigned char data[BUFFER_SIZE];
|
/* ts A61219:
|
||||||
|
Added 4096 bytes reserve against possible buffer overflows.
|
||||||
|
(Changed in sector.c buffer flush test from >= to > BUFFER_SIZE .
|
||||||
|
This can at most cause a 1 sector overlap. Sometimes an offset
|
||||||
|
of 16 byte is applied to the output data (in some RAW mode). ) */
|
||||||
|
unsigned char data[BUFFER_SIZE + 4096];
|
||||||
int sectors;
|
int sectors;
|
||||||
int bytes;
|
int bytes;
|
||||||
};
|
};
|
||||||
@ -55,7 +57,7 @@ struct command
|
|||||||
struct buffer *page;
|
struct buffer *page;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct scsi_inquiry_data
|
struct burn_scsi_inquiry_data
|
||||||
{
|
{
|
||||||
char vendor[9];
|
char vendor[9];
|
||||||
char product[17];
|
char product[17];
|
||||||
@ -63,6 +65,7 @@ struct scsi_inquiry_data
|
|||||||
int valid;
|
int valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct scsi_mode_data
|
struct scsi_mode_data
|
||||||
{
|
{
|
||||||
int buffer_size;
|
int buffer_size;
|
||||||
@ -78,6 +81,16 @@ struct scsi_mode_data
|
|||||||
int simulate;
|
int simulate;
|
||||||
int max_read_speed;
|
int max_read_speed;
|
||||||
int max_write_speed;
|
int max_write_speed;
|
||||||
|
|
||||||
|
/* ts A61021 */
|
||||||
|
int min_write_speed;
|
||||||
|
|
||||||
|
/* ts A61225 : Results from ACh GET PERFORMANCE, Type 03h
|
||||||
|
Speed values go into *_*_speed */
|
||||||
|
int min_end_lba;
|
||||||
|
int max_end_lba;
|
||||||
|
struct burn_speed_descriptor *speed_descriptors;
|
||||||
|
|
||||||
int cur_read_speed;
|
int cur_read_speed;
|
||||||
int cur_write_speed;
|
int cur_write_speed;
|
||||||
int retry_page_length;
|
int retry_page_length;
|
||||||
@ -90,15 +103,36 @@ struct scsi_mode_data
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70112 : represents a single Formattable Capacity Descriptor as of
|
||||||
|
mmc5r03c.pdf 6.24.3.3 . There can at most be 32 of them. */
|
||||||
|
struct burn_format_descr {
|
||||||
|
/* format type: e.g 0x00 is "Full", 0x15 is "Quick" */
|
||||||
|
int type;
|
||||||
|
|
||||||
|
/* the size in bytes derived from Number of Blocks */
|
||||||
|
off_t size;
|
||||||
|
|
||||||
|
/* the Type Dependent Parameter (usually the write alignment size) */
|
||||||
|
unsigned tdp;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define LIBBURN_SG_MAX_SIBLINGS 16
|
||||||
|
|
||||||
/** Gets initialized in enumerate_common() and burn_drive_register() */
|
/** Gets initialized in enumerate_common() and burn_drive_register() */
|
||||||
struct burn_drive
|
struct burn_drive
|
||||||
{
|
{
|
||||||
|
int bus_no;
|
||||||
int host;
|
int host;
|
||||||
int id;
|
int id;
|
||||||
int channel;
|
int channel;
|
||||||
int lun;
|
int lun;
|
||||||
char *devname;
|
char *devname;
|
||||||
int fd;
|
|
||||||
|
|
||||||
|
/* see os.h for name of particular os-*.h where this is defined */
|
||||||
|
BURN_OS_TRANSPORT_DRIVE_ELEMENTS
|
||||||
|
|
||||||
|
|
||||||
/* ts A60904 : ticket 62, contribution by elmom */
|
/* ts A60904 : ticket 62, contribution by elmom */
|
||||||
/**
|
/**
|
||||||
@ -111,12 +145,69 @@ struct burn_drive
|
|||||||
|
|
||||||
enum burn_disc_status status;
|
enum burn_disc_status status;
|
||||||
int erasable;
|
int erasable;
|
||||||
|
|
||||||
|
/* ts A61201 from 46h GET CONFIGURATION */
|
||||||
|
int current_profile;
|
||||||
|
char current_profile_text[80];
|
||||||
|
int current_is_cd_profile;
|
||||||
|
int current_is_supported_profile;
|
||||||
|
|
||||||
|
/* ts A70128 : MMC-to-MMC feature info from 46h for DVD-RW.
|
||||||
|
Quite internal. Regard as opaque :)
|
||||||
|
*/
|
||||||
|
/* 1 = incremental recording available, 0 = not available */
|
||||||
|
int current_has_feat21h;
|
||||||
|
|
||||||
|
/* Link Size item number 0 from feature 0021h descriptor */
|
||||||
|
int current_feat21h_link_size;
|
||||||
|
|
||||||
|
/* Flags from feature 002Fh feature descriptor mmc5r03c.pdf 5.3.25 :
|
||||||
|
bit1= DVD-RW supported
|
||||||
|
bit2= Test Write available
|
||||||
|
bit3= DVD-R DL supported
|
||||||
|
bit6= Buffer Under-run Free recording available (page 05h BUFE)
|
||||||
|
Value -1 indicates that no 002Fh was current in the features list.
|
||||||
|
*/
|
||||||
|
int current_feat2fh_byte4;
|
||||||
|
|
||||||
|
/* ts A70114 : wether a DVD-RW media holds an incomplete session
|
||||||
|
(which could need closing after write) */
|
||||||
|
int needs_close_session;
|
||||||
|
|
||||||
|
/* ts A61218 from 51h READ DISC INFORMATION */
|
||||||
|
int bg_format_status; /* 0=needs format start, 1=needs format restart*/
|
||||||
|
|
||||||
|
/* ts A70108 from 23h READ FORMAT CAPACITY mmc5r03c.pdf 6.24 */
|
||||||
|
int format_descr_type; /* 1=unformatted, 2=formatted, 3=unclear */
|
||||||
|
off_t format_curr_max_size; /* meaning depends on format_descr_type */
|
||||||
|
unsigned format_curr_blsas; /* meaning depends on format_descr_type */
|
||||||
|
int best_format_type;
|
||||||
|
off_t best_format_size;
|
||||||
|
|
||||||
|
/* The complete list of format descriptors as read with 23h */
|
||||||
|
int num_format_descr;
|
||||||
|
struct burn_format_descr format_descriptors[32];
|
||||||
|
|
||||||
|
|
||||||
volatile int released;
|
volatile int released;
|
||||||
|
|
||||||
|
/* ts A61106 */
|
||||||
|
int silent_on_scsi_error;
|
||||||
|
|
||||||
int nwa; /* next writeable address */
|
int nwa; /* next writeable address */
|
||||||
int alba; /* absolute lba */
|
int alba; /* absolute lba */
|
||||||
int rlba; /* relative lba in section */
|
int rlba; /* relative lba in section */
|
||||||
int start_lba;
|
int start_lba;
|
||||||
int end_lba;
|
int end_lba;
|
||||||
|
|
||||||
|
|
||||||
|
/* ts A70131 : from 51h READ DISC INFORMATION Number of Sessions (-1)*/
|
||||||
|
int complete_sessions;
|
||||||
|
/* ts A70129 :
|
||||||
|
from 51h READ DISC INFORMATION Last Track Number in Last Session */
|
||||||
|
int last_track_no;
|
||||||
|
|
||||||
|
|
||||||
int toc_temp;
|
int toc_temp;
|
||||||
struct burn_disc *disc; /* disc structure */
|
struct burn_disc *disc; /* disc structure */
|
||||||
int block_types[4];
|
int block_types[4];
|
||||||
@ -128,17 +219,26 @@ struct burn_drive
|
|||||||
/* transport functions */
|
/* transport functions */
|
||||||
int (*grab) (struct burn_drive *);
|
int (*grab) (struct burn_drive *);
|
||||||
int (*release) (struct burn_drive *);
|
int (*release) (struct burn_drive *);
|
||||||
|
|
||||||
|
/* ts A61021 */
|
||||||
|
int (*drive_is_open) (struct burn_drive *);
|
||||||
|
|
||||||
int (*issue_command) (struct burn_drive *, struct command *);
|
int (*issue_command) (struct burn_drive *, struct command *);
|
||||||
|
|
||||||
/* lower level functions */
|
/* lower level functions */
|
||||||
void (*erase) (struct burn_drive *, int);
|
void (*erase) (struct burn_drive *, int);
|
||||||
void (*getcaps) (struct burn_drive *);
|
void (*getcaps) (struct burn_drive *);
|
||||||
|
|
||||||
|
/* ts A61021 */
|
||||||
|
void (*read_atip) (struct burn_drive *);
|
||||||
|
|
||||||
int (*write) (struct burn_drive *, int, struct buffer *);
|
int (*write) (struct burn_drive *, int, struct buffer *);
|
||||||
void (*read_toc) (struct burn_drive *);
|
void (*read_toc) (struct burn_drive *);
|
||||||
void (*lock) (struct burn_drive *);
|
void (*lock) (struct burn_drive *);
|
||||||
void (*unlock) (struct burn_drive *);
|
void (*unlock) (struct burn_drive *);
|
||||||
void (*eject) (struct burn_drive *);
|
void (*eject) (struct burn_drive *);
|
||||||
void (*load) (struct burn_drive *);
|
void (*load) (struct burn_drive *);
|
||||||
|
int (*start_unit) (struct burn_drive *);
|
||||||
void (*read_disc_info) (struct burn_drive *);
|
void (*read_disc_info) (struct burn_drive *);
|
||||||
void (*read_sectors) (struct burn_drive *,
|
void (*read_sectors) (struct burn_drive *,
|
||||||
int start,
|
int start,
|
||||||
@ -151,19 +251,50 @@ struct burn_drive
|
|||||||
void (*send_write_parameters) (struct burn_drive *,
|
void (*send_write_parameters) (struct burn_drive *,
|
||||||
const struct burn_write_opts *);
|
const struct burn_write_opts *);
|
||||||
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
void (*send_cue_sheet) (struct burn_drive *, struct cue_sheet *);
|
||||||
|
|
||||||
|
/* ts A70205 : Announce size of a DVD-R[W] DAO session. */
|
||||||
|
int (*reserve_track) (struct burn_drive *d, off_t size);
|
||||||
|
|
||||||
void (*sync_cache) (struct burn_drive *);
|
void (*sync_cache) (struct burn_drive *);
|
||||||
int (*get_erase_progress) (struct burn_drive *);
|
int (*get_erase_progress) (struct burn_drive *);
|
||||||
int (*get_nwa) (struct burn_drive *);
|
int (*get_nwa) (struct burn_drive *, int trackno, int *lba, int *nwa);
|
||||||
void (*close_disc) (struct burn_drive * d, struct burn_write_opts * o);
|
|
||||||
void (*close_session) (struct burn_drive * d,
|
/* ts A70131 : obtain (possibly fake) TOC number and start lba of
|
||||||
|
first track in last complete session */
|
||||||
|
int (*read_multi_session_c1)(struct burn_drive *d,
|
||||||
|
int *trackno, int *start);
|
||||||
|
|
||||||
|
/* ts A61009 : removed d in favor of o->drive */
|
||||||
|
/* void (*close_disc) (struct burn_drive * d,
|
||||||
|
struct burn_write_opts * o);
|
||||||
|
void (*close_session) (struct burn_drive * d,
|
||||||
struct burn_write_opts * o);
|
struct burn_write_opts * o);
|
||||||
|
*/
|
||||||
|
void (*close_disc) (struct burn_write_opts * o);
|
||||||
|
void (*close_session) ( struct burn_write_opts * o);
|
||||||
|
|
||||||
|
/* ts A61029 */
|
||||||
|
void (*close_track_session) ( struct burn_drive *d,
|
||||||
|
int session, int track);
|
||||||
|
|
||||||
int (*test_unit_ready) (struct burn_drive * d);
|
int (*test_unit_ready) (struct burn_drive * d);
|
||||||
void (*probe_write_modes) (struct burn_drive * d);
|
void (*probe_write_modes) (struct burn_drive * d);
|
||||||
struct params params;
|
struct params params;
|
||||||
struct scsi_inquiry_data *idata;
|
struct burn_scsi_inquiry_data *idata;
|
||||||
struct scsi_mode_data *mdata;
|
struct scsi_mode_data *mdata;
|
||||||
int toc_entries;
|
int toc_entries;
|
||||||
struct burn_toc_entry *toc_entry;
|
struct burn_toc_entry *toc_entry;
|
||||||
|
|
||||||
|
/* ts A61023 : get size and free space of drive buffer */
|
||||||
|
int (*read_buffer_capacity) (struct burn_drive *d);
|
||||||
|
|
||||||
|
/* ts A61220 : format media (e.g. DVD+RW) */
|
||||||
|
int (*format_unit) (struct burn_drive *d, off_t size, int flag);
|
||||||
|
|
||||||
|
/* ts A70108 */
|
||||||
|
/* mmc5r03c.pdf 6.24 : get list of available formats */
|
||||||
|
int (*read_format_capacities) (struct burn_drive *d, int top_wanted);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* end of generic 'drive' data structures */
|
/* end of generic 'drive' data structures */
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
|
/* ts A61008 */
|
||||||
|
/* #include <a ssert.h> */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../version.h"
|
#include "../version.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -10,7 +13,10 @@ char *burn_strdup(char *s)
|
|||||||
char *ret;
|
char *ret;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
assert(s);
|
/* ts A61008 */
|
||||||
|
/* a ssert(s); */
|
||||||
|
if (s == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
l = strlen(s) + 1;
|
l = strlen(s) + 1;
|
||||||
ret = malloc(l);
|
ret = malloc(l);
|
||||||
@ -24,8 +30,11 @@ char *burn_strndup(char *s, int n)
|
|||||||
char *ret;
|
char *ret;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
assert(s);
|
/* ts A61008 */
|
||||||
assert(n > 0);
|
/* a ssert(s); */
|
||||||
|
/* a ssert(n > 0); */
|
||||||
|
if (s == NULL || n <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
l = strlen(s);
|
l = strlen(s);
|
||||||
ret = malloc(l < n ? l : n);
|
ret = malloc(l < n ? l : n);
|
||||||
|
1270
libburn/write.c
1270
libburn/write.c
File diff suppressed because it is too large
Load Diff
@ -9,9 +9,14 @@ struct burn_write_opts;
|
|||||||
struct burn_disc;
|
struct burn_disc;
|
||||||
|
|
||||||
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
struct cue_sheet *burn_create_toc_entries(struct burn_write_opts *o,
|
||||||
struct burn_session *session);
|
struct burn_session *session,
|
||||||
|
int nwa);
|
||||||
int burn_sector_length(int trackmode);
|
int burn_sector_length(int trackmode);
|
||||||
int burn_subcode_length(int trackmode);
|
int burn_subcode_length(int trackmode);
|
||||||
|
|
||||||
|
/* ts A61009 */
|
||||||
|
int burn_disc_write_is_ok(struct burn_write_opts *o, struct burn_disc *disc);
|
||||||
|
|
||||||
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc);
|
void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc);
|
||||||
int burn_write_leadin(struct burn_write_opts *o,
|
int burn_write_leadin(struct burn_write_opts *o,
|
||||||
struct burn_session *s, int first);
|
struct burn_session *s, int first);
|
||||||
@ -20,6 +25,11 @@ int burn_write_leadout(struct burn_write_opts *o,
|
|||||||
int burn_write_session(struct burn_write_opts *o, struct burn_session *s);
|
int burn_write_session(struct burn_write_opts *o, struct burn_session *s);
|
||||||
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
int burn_write_track(struct burn_write_opts *o, struct burn_session *s,
|
||||||
int tnum);
|
int tnum);
|
||||||
int burn_write_flush(struct burn_write_opts *o);
|
int burn_write_flush(struct burn_write_opts *o, struct burn_track *track);
|
||||||
|
|
||||||
|
/* ts A61030 : necessary for TAO */
|
||||||
|
int burn_write_close_track(struct burn_write_opts *o, struct burn_session *s,
|
||||||
|
int tnum);
|
||||||
|
int burn_write_close_session(struct burn_write_opts *o,struct burn_session *s);
|
||||||
|
|
||||||
#endif /* BURN__WRITE_H */
|
#endif /* BURN__WRITE_H */
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
prefix=@prefix@
|
|
||||||
exec_prefix=@exec_prefix@
|
|
||||||
libdir=@libdir@
|
|
||||||
includedir=@includedir@
|
|
||||||
|
|
||||||
Name: libisofs
|
|
||||||
Description: ISO9660 filesystem creation library
|
|
||||||
Version: @VERSION@
|
|
||||||
Requires:
|
|
||||||
Libs: -L${libdir} -lisofs
|
|
||||||
Cflags: -I${includedir}/libburn
|
|
@ -1,4 +0,0 @@
|
|||||||
all clean:
|
|
||||||
$(MAKE) -C .. -$(MAKEFLAGS) $@
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
@ -1,49 +0,0 @@
|
|||||||
pkgconfigdir=$(libdir)/pkgconfig
|
|
||||||
libincludedir=$(includedir)/libburn
|
|
||||||
|
|
||||||
##bin_PROGRAMS = test
|
|
||||||
|
|
||||||
lib_LTLIBRARIES = libisofs.la
|
|
||||||
|
|
||||||
libisofs_la_SOURCES = \
|
|
||||||
tree.h \
|
|
||||||
tree.c \
|
|
||||||
volume.h \
|
|
||||||
volume.c \
|
|
||||||
util.h \
|
|
||||||
util.c \
|
|
||||||
ecma119.c \
|
|
||||||
ecma119.h \
|
|
||||||
ecma119_tree.c \
|
|
||||||
ecma119_tree.h \
|
|
||||||
susp.h \
|
|
||||||
susp.c \
|
|
||||||
rockridge.h \
|
|
||||||
rockridge.c \
|
|
||||||
joliet.c \
|
|
||||||
joliet.h
|
|
||||||
|
|
||||||
libinclude_HEADERS = libisofs.h
|
|
||||||
|
|
||||||
##test_SOURCES = test.c
|
|
||||||
##test_LDADD = libisofs.la
|
|
||||||
|
|
||||||
##noinst_PROGRAMS = test
|
|
||||||
##test_SOURCES = test.c
|
|
||||||
##test_LDADD = $(libisofs_la_OBJECTS)
|
|
||||||
|
|
||||||
##INCLUDES = -I../burn/libburn
|
|
||||||
|
|
||||||
## ========================================================================= ##
|
|
||||||
indent_files = $(libisofs_la_SOURCES)
|
|
||||||
|
|
||||||
indent: $(indent_files)
|
|
||||||
indent -bad -bap -nbbb -nbbo -nbc -bli0 -br -bls \
|
|
||||||
-cdw -ce -cli0 -ncs -nbfda -i8 -l79 -lc79 \
|
|
||||||
-lp -saf -sai -nprs -npsl -saw -sob -ss -ut \
|
|
||||||
-sbi0 -nsc -ts8 -npcs -ncdb -fca \
|
|
||||||
$^
|
|
||||||
|
|
||||||
.PHONY: indent
|
|
||||||
|
|
||||||
## ========================================================================= ##
|
|
@ -1,694 +0,0 @@
|
|||||||
/* -*- 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 "libisofs.h"
|
|
||||||
#include "libburn/libburn.h"
|
|
||||||
|
|
||||||
/* burn-source compatible stuff */
|
|
||||||
static int
|
|
||||||
bs_read(struct burn_source *bs, unsigned char *buf, int size);
|
|
||||||
static off_t
|
|
||||||
bs_get_size(struct burn_source *bs);
|
|
||||||
static void
|
|
||||||
bs_free_data(struct burn_source *bs);
|
|
||||||
|
|
||||||
typedef void (*write_fn)(struct ecma119_write_target*, uint8_t*);
|
|
||||||
|
|
||||||
/* return true if the given state is only required for Joliet volumes */
|
|
||||||
static int
|
|
||||||
is_joliet_state(enum ecma119_write_state);
|
|
||||||
|
|
||||||
static void
|
|
||||||
next_state(struct ecma119_write_target *t);
|
|
||||||
|
|
||||||
/* write t->state_data to the buf, one block at a time */
|
|
||||||
static void
|
|
||||||
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
/* writing functions. All these functions assume the buf is large enough */
|
|
||||||
static void
|
|
||||||
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_one_dir_record(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir,
|
|
||||||
int file_id,
|
|
||||||
uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_one_dir(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir,
|
|
||||||
uint8_t *buf);
|
|
||||||
static void
|
|
||||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
/* wrapper functions for writing */
|
|
||||||
static void wr_system_area(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_pri_vol_desc(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_vol_desc_term(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_l_path_table(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_m_path_table(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_dir_records(struct ecma119_write_target*, uint8_t*);
|
|
||||||
static void wr_files(struct ecma119_write_target*, uint8_t*);
|
|
||||||
|
|
||||||
static const write_fn writers[] =
|
|
||||||
{
|
|
||||||
NULL,
|
|
||||||
wr_system_area,
|
|
||||||
wr_pri_vol_desc,
|
|
||||||
joliet_wr_sup_vol_desc,
|
|
||||||
wr_vol_desc_term,
|
|
||||||
wr_l_path_table,
|
|
||||||
wr_m_path_table,
|
|
||||||
joliet_wr_l_path_table,
|
|
||||||
joliet_wr_m_path_table,
|
|
||||||
wr_dir_records,
|
|
||||||
joliet_wr_dir_records,
|
|
||||||
wr_files
|
|
||||||
};
|
|
||||||
|
|
||||||
/* When a writer is created, we
|
|
||||||
* 1) create an ecma119 tree
|
|
||||||
* 2) add SUSP fields (if necessary)
|
|
||||||
* 3) calculate the size and position of all nodes in the tree
|
|
||||||
* 4) finalize SUSP fields (if necessary)
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_susp_fields_rec(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (!node->iso_self)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rrip_add_PX(t, node);
|
|
||||||
rrip_add_NM(t, node);
|
|
||||||
rrip_add_TF(t, node);
|
|
||||||
if (node->iso_self->attrib.st_rdev)
|
|
||||||
rrip_add_PN(t, node);
|
|
||||||
if (S_ISLNK(node->iso_self->attrib.st_mode))
|
|
||||||
rrip_add_SL(t, node);
|
|
||||||
if (node->type == ECMA119_FILE && node->file.real_me)
|
|
||||||
rrip_add_CL(t, node);
|
|
||||||
if (node->type == ECMA119_DIR
|
|
||||||
&& node->dir.real_parent != node->parent) {
|
|
||||||
rrip_add_RE(t, node);
|
|
||||||
rrip_add_PL(t, node);
|
|
||||||
}
|
|
||||||
susp_add_CE(t, node);
|
|
||||||
|
|
||||||
if (node->type == ECMA119_DIR) {
|
|
||||||
for (i = 0; i < node->dir.nchildren; i++) {
|
|
||||||
add_susp_fields_rec(t, node->dir.children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_susp_fields(struct ecma119_write_target *t)
|
|
||||||
{
|
|
||||||
susp_add_SP(t, t->root);
|
|
||||||
rrip_add_ER(t, t->root);
|
|
||||||
add_susp_fields_rec(t, t->root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill out the dir.len and dir.CE_len fields for each
|
|
||||||
* ecma119_tree_node that is a directory. Also calculate the total number of
|
|
||||||
* directories and the number of files for which we need to write out data.
|
|
||||||
* (dirlist_len and filelist_len)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
calc_dir_size(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
t->dirlist_len++;
|
|
||||||
dir->dir.len = 34 + dir->dir.self_susp.non_CE_len
|
|
||||||
+ 34 + dir->dir.parent_susp.non_CE_len;
|
|
||||||
dir->dir.CE_len = dir->dir.self_susp.CE_len
|
|
||||||
+ dir->dir.parent_susp.CE_len;
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
|
|
||||||
dir->dir.len += ch->dirent_len + ch->susp.non_CE_len;
|
|
||||||
dir->dir.CE_len += ch->susp.CE_len;
|
|
||||||
}
|
|
||||||
t->total_dir_size += round_up(dir->dir.len + dir->dir.CE_len,
|
|
||||||
t->block_size);
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
struct iso_tree_node *iso = ch->iso_self;
|
|
||||||
if (ch->type == ECMA119_DIR) {
|
|
||||||
calc_dir_size(t, ch);
|
|
||||||
} else if (iso && iso->attrib.st_size
|
|
||||||
&& iso->loc.type == LIBISO_FILESYS
|
|
||||||
&& iso->loc.path) {
|
|
||||||
t->filelist_len++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill out the block field in each ecma119_tree_node that is a directory and
|
|
||||||
* fill out t->dirlist.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
calc_dir_pos(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
/* we don't need to set iso_self->block since each tree writes
|
|
||||||
* its own directories */
|
|
||||||
dir->block = t->curblock;
|
|
||||||
t->curblock += div_up(dir->dir.len + dir->dir.CE_len, t->block_size);
|
|
||||||
t->dirlist[t->curfile++] = dir;
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
if (ch->type == ECMA119_DIR)
|
|
||||||
calc_dir_pos(t, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset curfile when we're finished */
|
|
||||||
if (!dir->parent) {
|
|
||||||
t->curfile = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill out the block field for each ecma119_tree_node that is a file and fill
|
|
||||||
* out t->filelist.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
calc_file_pos(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
if (ch->type == ECMA119_FILE && ch->iso_self) {
|
|
||||||
struct iso_tree_node *iso = ch->iso_self;
|
|
||||||
off_t size = iso->attrib.st_size;
|
|
||||||
|
|
||||||
iso->block = ch->block = t->curblock;
|
|
||||||
t->curblock += div_up(size, t->block_size);
|
|
||||||
if (size && iso->loc.type == LIBISO_FILESYS
|
|
||||||
&& iso->loc.path)
|
|
||||||
t->filelist[t->curfile++] = ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
if (ch->type == ECMA119_DIR)
|
|
||||||
calc_file_pos(t, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset curfile when we're finished */
|
|
||||||
if (!dir->parent) {
|
|
||||||
t->curfile = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ecma119_write_target*
|
|
||||||
ecma119_target_new(struct iso_volset *volset,
|
|
||||||
int volnum,
|
|
||||||
int level,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
struct ecma119_write_target *t =
|
|
||||||
calloc(1, sizeof(struct ecma119_write_target));
|
|
||||||
size_t i, j, cur;
|
|
||||||
struct iso_tree_node *iso_root = volset->volume[volnum]->root;
|
|
||||||
|
|
||||||
volset->refcount++;
|
|
||||||
t->root = ecma119_tree_create(t, iso_root);
|
|
||||||
t->joliet = (flags & ECMA119_JOLIET) ? 1 : 0;
|
|
||||||
if (t->joliet)
|
|
||||||
t->joliet_root = joliet_tree_create(t, iso_root);
|
|
||||||
t->volset = volset;
|
|
||||||
t->volnum = volnum;
|
|
||||||
t->now = time(NULL);
|
|
||||||
|
|
||||||
t->rockridge = (flags & ECMA119_ROCKRIDGE) ? 1 : 0;
|
|
||||||
t->iso_level = level;
|
|
||||||
t->block_size = 2048;
|
|
||||||
|
|
||||||
if (t->rockridge)
|
|
||||||
add_susp_fields(t);
|
|
||||||
calc_dir_size(t, t->root);
|
|
||||||
if (t->joliet) {
|
|
||||||
joliet_calc_dir_size(t, t->joliet_root);
|
|
||||||
t->pathlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
|
|
||||||
t->dirlist_joliet = calloc(1, sizeof(void*) * t->dirlist_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
t->dirlist = calloc(1, sizeof(void*) * t->dirlist_len);
|
|
||||||
t->pathlist = calloc(1, sizeof(void*) * t->dirlist_len);
|
|
||||||
t->filelist = calloc(1, sizeof(void*) * t->filelist_len);
|
|
||||||
|
|
||||||
/* fill out the pathlist */
|
|
||||||
t->pathlist[0] = t->root;
|
|
||||||
t->path_table_size = 10; /* root directory record */
|
|
||||||
cur = 1;
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
struct ecma119_tree_node *dir = t->pathlist[i];
|
|
||||||
for (j = 0; j < dir->dir.nchildren; j++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[j];
|
|
||||||
if (ch->type == ECMA119_DIR) {
|
|
||||||
size_t len = 8 + strlen(ch->name);
|
|
||||||
t->pathlist[cur++] = ch;
|
|
||||||
t->path_table_size += len + len % 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t->curblock = 16 /* system area */
|
|
||||||
+ 1 /* volume desc */
|
|
||||||
+ 1; /* volume desc terminator */
|
|
||||||
|
|
||||||
if (t->joliet) /* supplementary vol desc */
|
|
||||||
t->curblock += div_up (2048, t->block_size);
|
|
||||||
|
|
||||||
t->l_path_table_pos = t->curblock;
|
|
||||||
t->curblock += div_up(t->path_table_size, t->block_size);
|
|
||||||
t->m_path_table_pos = t->curblock;
|
|
||||||
t->curblock += div_up(t->path_table_size, t->block_size);
|
|
||||||
if (t->joliet) {
|
|
||||||
joliet_prepare_path_tables(t);
|
|
||||||
t->l_path_table_pos_joliet = t->curblock;
|
|
||||||
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
|
|
||||||
t->m_path_table_pos_joliet = t->curblock;
|
|
||||||
t->curblock += div_up(t->path_table_size_joliet, t->block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
calc_dir_pos(t, t->root);
|
|
||||||
if (t->joliet)
|
|
||||||
joliet_calc_dir_pos(t, t->joliet_root);
|
|
||||||
calc_file_pos(t, t->root);
|
|
||||||
if (t->joliet)
|
|
||||||
joliet_update_file_pos (t, t->joliet_root);
|
|
||||||
|
|
||||||
if (t->rockridge) {
|
|
||||||
susp_finalize(t, t->root);
|
|
||||||
rrip_finalize(t, t->root);
|
|
||||||
}
|
|
||||||
|
|
||||||
t->total_size = t->curblock * t->block_size;
|
|
||||||
t->vol_space_size = t->curblock;
|
|
||||||
|
|
||||||
/* prepare for writing */
|
|
||||||
t->curblock = 0;
|
|
||||||
t->state = ECMA119_WRITE_SYSTEM_AREA;
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
is_joliet_state(enum ecma119_write_state state)
|
|
||||||
{
|
|
||||||
return state == ECMA119_WRITE_SUP_VOL_DESC_JOLIET
|
|
||||||
|| state == ECMA119_WRITE_L_PATH_TABLE_JOLIET
|
|
||||||
|| state == ECMA119_WRITE_M_PATH_TABLE_JOLIET
|
|
||||||
|| state == ECMA119_WRITE_DIR_RECORDS_JOLIET;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
next_state(struct ecma119_write_target *t)
|
|
||||||
{
|
|
||||||
t->state++;
|
|
||||||
while (!t->joliet && is_joliet_state(t->state))
|
|
||||||
t->state++;
|
|
||||||
|
|
||||||
printf ("now in state %d, curblock=%d\n", (int)t->state, (int)t->curblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_system_area(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
memset(buf, 0, t->block_size);
|
|
||||||
if (t->curblock == 15) {
|
|
||||||
next_state(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void
|
|
||||||
wr_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t, write_pri_vol_desc, 2048, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_vol_desc_term(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t, write_vol_desc_terminator, 2048, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t, write_l_path_table, t->path_table_size, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t, write_m_path_table, t->path_table_size, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_dir_records(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t, write_dirs, t->total_dir_size, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
wr_files(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
struct state_files *f_st = &t->state_files;
|
|
||||||
size_t nread;
|
|
||||||
struct ecma119_tree_node *f = t->filelist[f_st->file];
|
|
||||||
const char *path = f->iso_self->loc.path;
|
|
||||||
|
|
||||||
if (!f_st->fd) {
|
|
||||||
f_st->data_len = f->iso_self->attrib.st_size;
|
|
||||||
f_st->fd = fopen(path, "r");
|
|
||||||
if (!f_st->fd)
|
|
||||||
err(1, "couldn't open %s for reading", path);
|
|
||||||
assert(t->curblock == f->block);
|
|
||||||
}
|
|
||||||
|
|
||||||
nread = fread(buf, 1, t->block_size, f_st->fd);
|
|
||||||
f_st->pos += t->block_size;
|
|
||||||
if (nread < 0)
|
|
||||||
warn("problem reading from %s", path);
|
|
||||||
else if (nread != t->block_size && f_st->pos < f_st->data_len)
|
|
||||||
warnx("incomplete read from %s", path);
|
|
||||||
if (f_st->pos >= f_st->data_len) {
|
|
||||||
fclose(f_st->fd);
|
|
||||||
f_st->fd = 0;
|
|
||||||
f_st->pos = 0;
|
|
||||||
f_st->file++;
|
|
||||||
if (f_st->file >= t->filelist_len)
|
|
||||||
next_state(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_pri_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
struct ecma119_pri_vol_desc *vol = (struct ecma119_pri_vol_desc*)buf;
|
|
||||||
struct iso_volume *volume = t->volset->volume[t->volnum];
|
|
||||||
char *vol_id = str2ascii(volume->volume_id);
|
|
||||||
char *pub_id = str2ascii(volume->publisher_id);
|
|
||||||
char *data_id = str2ascii(volume->data_preparer_id);
|
|
||||||
char *volset_id = str2ascii(t->volset->volset_id);
|
|
||||||
|
|
||||||
vol->vol_desc_type[0] = 1;
|
|
||||||
memcpy(vol->std_identifier, "CD001", 5);
|
|
||||||
vol->vol_desc_version[0] = 1;
|
|
||||||
memcpy(vol->system_id, "SYSID", 5);
|
|
||||||
if (vol_id)
|
|
||||||
strncpy((char*)vol->volume_id, vol_id, 32);
|
|
||||||
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
|
|
||||||
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
|
|
||||||
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
|
|
||||||
iso_bb(vol->block_size, t->block_size, 2);
|
|
||||||
iso_bb(vol->path_table_size, t->path_table_size, 4);
|
|
||||||
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos, 4);
|
|
||||||
iso_msb(vol->m_path_table_pos, t->m_path_table_pos, 4);
|
|
||||||
|
|
||||||
write_one_dir_record(t, t->root, 3, vol->root_dir_record);
|
|
||||||
|
|
||||||
strncpy((char*)vol->vol_set_id, volset_id, 128);
|
|
||||||
strncpy((char*)vol->publisher_id, pub_id, 128);
|
|
||||||
strncpy((char*)vol->data_prep_id, data_id, 128);
|
|
||||||
strncpy((char*)vol->application_id, "APPID", 128);
|
|
||||||
|
|
||||||
iso_datetime_17(vol->vol_creation_time, t->now);
|
|
||||||
iso_datetime_17(vol->vol_modification_time, t->now);
|
|
||||||
iso_datetime_17(vol->vol_effective_time, t->now);
|
|
||||||
vol->file_structure_version[0] = 1;
|
|
||||||
|
|
||||||
free(vol_id);
|
|
||||||
free(volset_id);
|
|
||||||
free(pub_id);
|
|
||||||
free(data_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_vol_desc_terminator(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
struct ecma119_vol_desc_terminator *vol =
|
|
||||||
(struct ecma119_vol_desc_terminator*) buf;
|
|
||||||
|
|
||||||
vol->vol_desc_type[0] = 255;
|
|
||||||
memcpy(vol->std_identifier, "CD001", 5);
|
|
||||||
vol->vol_desc_version[0] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_path_table(struct ecma119_write_target *t, int l_type, uint8_t *buf)
|
|
||||||
{
|
|
||||||
void (*write_int)(uint8_t*, uint32_t, int) = l_type ? iso_lsb
|
|
||||||
: iso_msb;
|
|
||||||
size_t i;
|
|
||||||
struct ecma119_path_table_record *rec;
|
|
||||||
struct ecma119_tree_node *dir;
|
|
||||||
int parent = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
dir = t->pathlist[i];
|
|
||||||
while ((i) && t->pathlist[parent] != dir->parent)
|
|
||||||
parent++;
|
|
||||||
assert(parent < i || i == 0);
|
|
||||||
|
|
||||||
rec = (struct ecma119_path_table_record*) buf;
|
|
||||||
rec->len_di[0] = dir->parent ? (uint8_t) strlen(dir->name) : 1;
|
|
||||||
rec->len_xa[0] = 0;
|
|
||||||
write_int(rec->block, dir->block, 4);
|
|
||||||
write_int(rec->parent, parent + 1, 2);
|
|
||||||
if (dir->parent)
|
|
||||||
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
|
|
||||||
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
write_path_table(t, 1, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
write_path_table(t, 0, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
|
|
||||||
* file_id == 3 means that we are writing the root directory record (in order
|
|
||||||
* to distinguish it from the "." entry in the root directory) */
|
|
||||||
static void
|
|
||||||
write_one_dir_record(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *node,
|
|
||||||
int file_id,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
|
|
||||||
uint8_t len_fi = (file_id >= 0) ? 1 : strlen(node->name);
|
|
||||||
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
|
|
||||||
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
|
|
||||||
uint32_t len = (node->type == ECMA119_DIR) ? node->dir.len
|
|
||||||
: node->file.real_me ? 0 : node->iso_self->attrib.st_size;
|
|
||||||
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
|
|
||||||
|
|
||||||
/* we don't write out susp fields for the root node */
|
|
||||||
if (t->rockridge) {
|
|
||||||
if (file_id == 0) {
|
|
||||||
susp_write(t, &node->dir.self_susp, &buf[len_dr]);
|
|
||||||
len_dr += node->dir.self_susp.non_CE_len;
|
|
||||||
} else if (file_id == 1) {
|
|
||||||
susp_write(t, &node->dir.parent_susp, &buf[len_dr]);
|
|
||||||
len_dr += node->dir.parent_susp.non_CE_len;
|
|
||||||
} else if (file_id < 0) {
|
|
||||||
susp_write(t, &node->susp, &buf[len_dr]);
|
|
||||||
len_dr += node->susp.non_CE_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (file_id == 1 && node->parent)
|
|
||||||
node = node->parent;
|
|
||||||
|
|
||||||
rec->len_dr[0] = len_dr;
|
|
||||||
iso_bb(rec->block, node->block, 4);
|
|
||||||
iso_bb(rec->length, len, 4);
|
|
||||||
iso_datetime_7(rec->recording_time, t->now);
|
|
||||||
rec->flags[0] = (node->type == ECMA119_DIR) ? 2 : 0;
|
|
||||||
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
|
|
||||||
rec->len_fi[0] = len_fi;
|
|
||||||
memcpy(rec->file_id, name, len_fi);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_one_dir(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
uint8_t *orig_buf = buf;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
/* write the "." and ".." entries first */
|
|
||||||
write_one_dir_record(t, dir, 0, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
|
|
||||||
write_one_dir_record(t, dir, 1, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
write_one_dir_record(t, dir->dir.children[i], -1, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write the susp continuation areas */
|
|
||||||
if (t->rockridge) {
|
|
||||||
susp_write_CE(t, &dir->dir.self_susp, buf);
|
|
||||||
buf += dir->dir.self_susp.CE_len;
|
|
||||||
susp_write_CE(t, &dir->dir.parent_susp, buf);
|
|
||||||
buf += dir->dir.parent_susp.CE_len;
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
susp_write_CE(t, &dir->dir.children[i]->susp, buf);
|
|
||||||
buf += dir->dir.children[i]->susp.CE_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert (buf - orig_buf == dir->dir.len + dir->dir.CE_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
struct ecma119_tree_node *dir;
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
dir = t->dirlist[i];
|
|
||||||
write_one_dir(t, dir, buf);
|
|
||||||
buf += round_up(dir->dir.len + dir->dir.CE_len, t->block_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ecma119_start_chunking(struct ecma119_write_target *t,
|
|
||||||
write_fn writer,
|
|
||||||
off_t data_size,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
if (data_size != t->state_data_size) {
|
|
||||||
data_size = round_up(data_size, t->block_size);
|
|
||||||
t->state_data = realloc(t->state_data, data_size);
|
|
||||||
t->state_data_size = data_size;
|
|
||||||
}
|
|
||||||
memset(t->state_data, 0, t->state_data_size);
|
|
||||||
t->state_data_off = 0;
|
|
||||||
t->state_data_valid = 1;
|
|
||||||
writer(t, t->state_data);
|
|
||||||
write_data_chunk(t, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_data_chunk(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
memcpy(buf, t->state_data + t->state_data_off, t->block_size);
|
|
||||||
t->state_data_off += t->block_size;
|
|
||||||
if (t->state_data_off >= t->state_data_size) {
|
|
||||||
assert (t->state_data_off <= t->state_data_size);
|
|
||||||
t->state_data_valid = 0;
|
|
||||||
next_state(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
bs_read(struct burn_source *bs, unsigned char *buf, int size)
|
|
||||||
{
|
|
||||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
|
||||||
if (size != t->block_size) {
|
|
||||||
warnx("you must read data in block-sized chunks (%d bytes)",
|
|
||||||
(int)t->block_size);
|
|
||||||
return 0;
|
|
||||||
} else if (t->curblock >= t->vol_space_size) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (t->state_data_valid)
|
|
||||||
write_data_chunk(t, buf);
|
|
||||||
else
|
|
||||||
writers[t->state](t, buf);
|
|
||||||
t->curblock++;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static off_t
|
|
||||||
bs_get_size(struct burn_source *bs)
|
|
||||||
{
|
|
||||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
|
||||||
return t->total_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
bs_free_data(struct burn_source *bs)
|
|
||||||
{
|
|
||||||
struct ecma119_write_target *t = (struct ecma119_write_target*)bs->data;
|
|
||||||
ecma119_tree_free(t->root);
|
|
||||||
free(t->dirlist);
|
|
||||||
free(t->pathlist);
|
|
||||||
free(t->dirlist_joliet);
|
|
||||||
free(t->pathlist_joliet);
|
|
||||||
free(t->filelist);
|
|
||||||
free(t->state_data);
|
|
||||||
if (t->state_files.fd)
|
|
||||||
fclose(t->state_files.fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct burn_source *iso_source_new_ecma119(struct iso_volset *volset,
|
|
||||||
int volnum,
|
|
||||||
int level,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
struct burn_source *ret = calloc(1, sizeof(struct burn_source));
|
|
||||||
ret->refcount = 1;
|
|
||||||
ret->read = bs_read;
|
|
||||||
ret->get_size = bs_get_size;
|
|
||||||
ret->free_data = bs_free_data;
|
|
||||||
ret->data = ecma119_target_new(volset, volnum, level, flags);
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
/* -*- 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_SUP_VOL_DESC_JOLIET,
|
|
||||||
ECMA119_WRITE_VOL_DESC_TERMINATOR,
|
|
||||||
ECMA119_WRITE_L_PATH_TABLE,
|
|
||||||
ECMA119_WRITE_M_PATH_TABLE,
|
|
||||||
ECMA119_WRITE_L_PATH_TABLE_JOLIET,
|
|
||||||
ECMA119_WRITE_M_PATH_TABLE_JOLIET,
|
|
||||||
ECMA119_WRITE_DIR_RECORDS,
|
|
||||||
ECMA119_WRITE_DIR_RECORDS_JOLIET,
|
|
||||||
ECMA119_WRITE_FILES,
|
|
||||||
|
|
||||||
ECMA119_WRITE_DONE
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data describing the state of the ecma119 writer. Everything here should be
|
|
||||||
* considered private!
|
|
||||||
*/
|
|
||||||
struct ecma119_write_target
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *root;
|
|
||||||
struct joliet_tree_node *joliet_root;
|
|
||||||
struct iso_volset *volset;
|
|
||||||
int volnum;
|
|
||||||
|
|
||||||
time_t now; /**< Time at which writing began. */
|
|
||||||
off_t total_size; /**< Total size of the output. This only
|
|
||||||
* includes the current volume. */
|
|
||||||
uint32_t vol_space_size;
|
|
||||||
|
|
||||||
unsigned int rockridge:1;
|
|
||||||
unsigned int joliet:1;
|
|
||||||
unsigned int iso_level:2;
|
|
||||||
|
|
||||||
int curblock;
|
|
||||||
uint16_t block_size;
|
|
||||||
uint32_t path_table_size;
|
|
||||||
uint32_t path_table_size_joliet;
|
|
||||||
uint32_t l_path_table_pos;
|
|
||||||
uint32_t m_path_table_pos;
|
|
||||||
uint32_t l_path_table_pos_joliet;
|
|
||||||
uint32_t m_path_table_pos_joliet;
|
|
||||||
uint32_t total_dir_size;
|
|
||||||
uint32_t total_dir_size_joliet;
|
|
||||||
|
|
||||||
struct ecma119_tree_node **dirlist;
|
|
||||||
/**< A pre-order list of directories
|
|
||||||
* (this is the order in which we write
|
|
||||||
* out directory records).
|
|
||||||
*/
|
|
||||||
struct ecma119_tree_node **pathlist;
|
|
||||||
/**< A breadth-first list of
|
|
||||||
* directories. This is used for
|
|
||||||
* writing out the path tables.
|
|
||||||
*/
|
|
||||||
size_t dirlist_len; /**< The length of the previous 2 lists.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct ecma119_tree_node **filelist;
|
|
||||||
/**< A pre-order list of files with
|
|
||||||
* non-NULL paths and non-zero sizes.
|
|
||||||
*/
|
|
||||||
size_t filelist_len; /* Length of the previous list. */
|
|
||||||
|
|
||||||
int curfile; /**< Used as a helper field for writing
|
|
||||||
out filelist and dirlist */
|
|
||||||
|
|
||||||
/* Joliet versions of the above lists. Since Joliet doesn't require
|
|
||||||
* directory relocation, the order of these lists might be different
|
|
||||||
* from the lists above (but they will be the same length).
|
|
||||||
*/
|
|
||||||
struct joliet_tree_node **dirlist_joliet;
|
|
||||||
struct joliet_tree_node **pathlist_joliet;
|
|
||||||
|
|
||||||
enum ecma119_write_state state; /* The current state of the writer. */
|
|
||||||
|
|
||||||
/* Most writers work by
|
|
||||||
* 1) making sure state_data is big enough for their data
|
|
||||||
* 2) writing _all_ their data into state_data
|
|
||||||
* 3) relying on write_data_chunk to write the data block
|
|
||||||
* by block.
|
|
||||||
*/
|
|
||||||
uint8_t *state_data;
|
|
||||||
off_t state_data_size;
|
|
||||||
off_t state_data_off;
|
|
||||||
int state_data_valid;
|
|
||||||
|
|
||||||
/* for writing out files */
|
|
||||||
struct state_files {
|
|
||||||
off_t pos; /* The number of bytes we have written
|
|
||||||
* so far in the current file.
|
|
||||||
*/
|
|
||||||
off_t data_len;/* The number of bytes in the currently
|
|
||||||
* open file.
|
|
||||||
*/
|
|
||||||
FILE *fd; /* The currently open file. */
|
|
||||||
int file; /* The index in filelist that we are
|
|
||||||
* currently writing (or about to write). */
|
|
||||||
} state_files;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ecma119_write_target from the given volume number of the
|
|
||||||
* given volume set.
|
|
||||||
*
|
|
||||||
* \pre \p volnum is less than \p volset-\>volset_size.
|
|
||||||
* \post For each node in the tree, writer_data has been allocated.
|
|
||||||
* \post The directory heirarchy has been reorganised to be ecma119-compatible.
|
|
||||||
*/
|
|
||||||
struct ecma119_write_target *ecma119_target_new(struct iso_volset *volset,
|
|
||||||
int volnum,
|
|
||||||
int level,
|
|
||||||
int flags);
|
|
||||||
|
|
||||||
#define BP(a,b) [(b) - (a) + 1]
|
|
||||||
|
|
||||||
struct ecma119_pri_vol_desc
|
|
||||||
{
|
|
||||||
uint8_t vol_desc_type BP(1, 1);
|
|
||||||
uint8_t std_identifier BP(2, 6);
|
|
||||||
uint8_t vol_desc_version BP(7, 7);
|
|
||||||
uint8_t unused1 BP(8, 8);
|
|
||||||
uint8_t system_id BP(9, 40);
|
|
||||||
uint8_t volume_id BP(41, 72);
|
|
||||||
uint8_t unused2 BP(73, 80);
|
|
||||||
uint8_t vol_space_size BP(81, 88);
|
|
||||||
uint8_t unused3 BP(89, 120);
|
|
||||||
uint8_t vol_set_size BP(121, 124);
|
|
||||||
uint8_t vol_seq_number BP(125, 128);
|
|
||||||
uint8_t block_size BP(129, 132);
|
|
||||||
uint8_t path_table_size BP(133, 140);
|
|
||||||
uint8_t l_path_table_pos BP(141, 144);
|
|
||||||
uint8_t opt_l_path_table_pos BP(145, 148);
|
|
||||||
uint8_t m_path_table_pos BP(149, 152);
|
|
||||||
uint8_t opt_m_path_table_pos BP(153, 156);
|
|
||||||
uint8_t root_dir_record BP(157, 190);
|
|
||||||
uint8_t vol_set_id BP(191, 318);
|
|
||||||
uint8_t publisher_id BP(319, 446);
|
|
||||||
uint8_t data_prep_id BP(447, 574);
|
|
||||||
uint8_t application_id BP(575, 702);
|
|
||||||
uint8_t copyright_file_id BP(703, 739);
|
|
||||||
uint8_t abstract_file_id BP(740, 776);
|
|
||||||
uint8_t bibliographic_file_id BP(777, 813);
|
|
||||||
uint8_t vol_creation_time BP(814, 830);
|
|
||||||
uint8_t vol_modification_time BP(831, 847);
|
|
||||||
uint8_t vol_expiration_time BP(848, 864);
|
|
||||||
uint8_t vol_effective_time BP(865, 881);
|
|
||||||
uint8_t file_structure_version BP(882, 882);
|
|
||||||
uint8_t reserved1 BP(883, 883);
|
|
||||||
uint8_t app_use BP(884, 1395);
|
|
||||||
uint8_t reserved2 BP(1396, 2048);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_sup_vol_desc
|
|
||||||
{
|
|
||||||
uint8_t vol_desc_type BP(1, 1);
|
|
||||||
uint8_t std_identifier BP(2, 6);
|
|
||||||
uint8_t vol_desc_version BP(7, 7);
|
|
||||||
uint8_t vol_flags BP(8, 8);
|
|
||||||
uint8_t system_id BP(9, 40);
|
|
||||||
uint8_t volume_id BP(41, 72);
|
|
||||||
uint8_t unused2 BP(73, 80);
|
|
||||||
uint8_t vol_space_size BP(81, 88);
|
|
||||||
uint8_t esc_sequences BP(89, 120);
|
|
||||||
uint8_t vol_set_size BP(121, 124);
|
|
||||||
uint8_t vol_seq_number BP(125, 128);
|
|
||||||
uint8_t block_size BP(129, 132);
|
|
||||||
uint8_t path_table_size BP(133, 140);
|
|
||||||
uint8_t l_path_table_pos BP(141, 144);
|
|
||||||
uint8_t opt_l_path_table_pos BP(145, 148);
|
|
||||||
uint8_t m_path_table_pos BP(149, 152);
|
|
||||||
uint8_t opt_m_path_table_pos BP(153, 156);
|
|
||||||
uint8_t root_dir_record BP(157, 190);
|
|
||||||
uint8_t vol_set_id BP(191, 318);
|
|
||||||
uint8_t publisher_id BP(319, 446);
|
|
||||||
uint8_t data_prep_id BP(447, 574);
|
|
||||||
uint8_t application_id BP(575, 702);
|
|
||||||
uint8_t copyright_file_id BP(703, 739);
|
|
||||||
uint8_t abstract_file_id BP(740, 776);
|
|
||||||
uint8_t bibliographic_file_id BP(777, 813);
|
|
||||||
uint8_t vol_creation_time BP(814, 830);
|
|
||||||
uint8_t vol_modification_time BP(831, 847);
|
|
||||||
uint8_t vol_expiration_time BP(848, 864);
|
|
||||||
uint8_t vol_effective_time BP(865, 881);
|
|
||||||
uint8_t file_structure_version BP(882, 882);
|
|
||||||
uint8_t reserved1 BP(883, 883);
|
|
||||||
uint8_t app_use BP(884, 1395);
|
|
||||||
uint8_t reserved2 BP(1396, 2048);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_vol_desc_terminator
|
|
||||||
{
|
|
||||||
uint8_t vol_desc_type BP(1, 1);
|
|
||||||
uint8_t std_identifier BP(2, 6);
|
|
||||||
uint8_t vol_desc_version BP(7, 7);
|
|
||||||
uint8_t reserved BP(8, 2048);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_dir_record
|
|
||||||
{
|
|
||||||
uint8_t len_dr BP(1, 1);
|
|
||||||
uint8_t len_xa BP(2, 2);
|
|
||||||
uint8_t block BP(3, 10);
|
|
||||||
uint8_t length BP(11, 18);
|
|
||||||
uint8_t recording_time BP(19, 25);
|
|
||||||
uint8_t flags BP(26, 26);
|
|
||||||
uint8_t file_unit_size BP(27, 27);
|
|
||||||
uint8_t interleave_gap_size BP(28, 28);
|
|
||||||
uint8_t vol_seq_number BP(29, 32);
|
|
||||||
uint8_t len_fi BP(33, 33);
|
|
||||||
uint8_t file_id BP(34, 34); /* 34 to 33+len_fi */
|
|
||||||
/* padding field (if len_fi is even) */
|
|
||||||
/* system use (len_dr - len_su + 1 to len_dr) */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_path_table_record
|
|
||||||
{
|
|
||||||
uint8_t len_di BP(1, 1);
|
|
||||||
uint8_t len_xa BP(2, 2);
|
|
||||||
uint8_t block BP(3, 6);
|
|
||||||
uint8_t parent BP(7, 8);
|
|
||||||
uint8_t dir_id BP(9, 9); /* 9 to 8+len_di */
|
|
||||||
/* padding field (if len_di is odd) */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility function for writers that want to write their data all at once
|
|
||||||
* rather than block-by-block. This creates a buffer of size \p size, passes
|
|
||||||
* it to the given writer, then hands out block-sized chunks.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ecma119_start_chunking(struct ecma119_write_target *t,
|
|
||||||
void (*)(struct ecma119_write_target*, uint8_t*),
|
|
||||||
off_t size,
|
|
||||||
uint8_t *buf);
|
|
||||||
|
|
||||||
#endif /* LIBISO_ECMA119_H */
|
|
@ -1,312 +0,0 @@
|
|||||||
/* 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"
|
|
||||||
|
|
||||||
static size_t calc_dirent_len(struct ecma119_tree_node *n)
|
|
||||||
{
|
|
||||||
int ret = n->name ? strlen(n->name) + 33 : 34;
|
|
||||||
if (ret % 2) ret++;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ecma119_tree_node*
|
|
||||||
create_dir(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *parent,
|
|
||||||
struct iso_tree_node *iso)
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *ret;
|
|
||||||
|
|
||||||
assert(t && (!parent || parent->type == ECMA119_DIR)
|
|
||||||
&& iso && S_ISDIR(iso->attrib.st_mode));
|
|
||||||
|
|
||||||
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
|
||||||
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_dirid(iso->name)
|
|
||||||
: iso_2_dirid(iso->name))
|
|
||||||
: NULL;
|
|
||||||
ret->dirent_len = calc_dirent_len(ret);
|
|
||||||
ret->iso_self = iso;
|
|
||||||
ret->target = t;
|
|
||||||
ret->type = ECMA119_DIR;
|
|
||||||
ret->parent = ret->dir.real_parent = parent;
|
|
||||||
ret->dir.depth = parent ? parent->dir.depth + 1 : 1;
|
|
||||||
ret->dir.nchildren = iso->nchildren;
|
|
||||||
ret->dir.children = calloc(1, sizeof(void*) * iso->nchildren);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ecma119_tree_node*
|
|
||||||
create_file(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *parent,
|
|
||||||
struct iso_tree_node *iso)
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *ret;
|
|
||||||
|
|
||||||
assert(t && iso && parent && parent->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
ret = calloc(1, sizeof(struct ecma119_tree_node));
|
|
||||||
ret->name = iso->name ? ((t->iso_level == 1) ? iso_1_fileid(iso->name)
|
|
||||||
: iso_2_fileid(iso->name))
|
|
||||||
: NULL;
|
|
||||||
ret->dirent_len = calc_dirent_len(ret);
|
|
||||||
ret->parent = parent;
|
|
||||||
ret->iso_self = iso;
|
|
||||||
ret->target = t;
|
|
||||||
ret->type = ECMA119_FILE;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ecma119_tree_node*
|
|
||||||
create_tree(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *parent,
|
|
||||||
struct iso_tree_node *iso)
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *ret;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(t && iso);
|
|
||||||
|
|
||||||
ret = (S_ISDIR(iso->attrib.st_mode) ? create_dir : create_file)
|
|
||||||
(t, parent, iso);
|
|
||||||
for (i = 0; i < iso->nchildren; i++) {
|
|
||||||
ret->dir.children[i] = create_tree(t, ret, iso->children[i]);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ecma119_tree_free(struct ecma119_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (root->type == ECMA119_DIR) {
|
|
||||||
for (i=0; i < root->dir.nchildren; i++) {
|
|
||||||
ecma119_tree_free(root->dir.children[i]);
|
|
||||||
}
|
|
||||||
free(root->dir.children);
|
|
||||||
}
|
|
||||||
free(root->name);
|
|
||||||
free(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
max_child_name_len(struct ecma119_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t ret = 0, i;
|
|
||||||
|
|
||||||
assert(root->type == ECMA119_DIR);
|
|
||||||
for (i=0; i < root->dir.nchildren; i++) {
|
|
||||||
size_t len = strlen(root->dir.children[i]->name);
|
|
||||||
ret = MAX(ret, len);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
reparent(struct ecma119_tree_node *child,
|
|
||||||
struct ecma119_tree_node *parent)
|
|
||||||
{
|
|
||||||
int found = 0;
|
|
||||||
size_t i;
|
|
||||||
struct ecma119_tree_node *placeholder;
|
|
||||||
|
|
||||||
assert(child && parent && parent->type == ECMA119_DIR && child->parent);
|
|
||||||
|
|
||||||
/* replace the child in the original parent with a placeholder */
|
|
||||||
for (i=0; i < child->parent->dir.nchildren; i++) {
|
|
||||||
if (child->parent->dir.children[i] == child) {
|
|
||||||
placeholder = create_file(child->target,
|
|
||||||
child->parent,
|
|
||||||
child->iso_self);
|
|
||||||
placeholder->file.real_me = child;
|
|
||||||
child->parent->dir.children[i] = placeholder;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(found);
|
|
||||||
|
|
||||||
/* add the child to its new parent */
|
|
||||||
child->parent = parent;
|
|
||||||
parent->dir.nchildren++;
|
|
||||||
parent->dir.children = realloc( parent->dir.children,
|
|
||||||
sizeof(void*) * parent->dir.nchildren );
|
|
||||||
parent->dir.children[parent->dir.nchildren-1] = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorder the tree, if necessary, to ensure that
|
|
||||||
* - the depth is at most 8
|
|
||||||
* - each path length is at most 255 characters
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
reorder_tree(struct ecma119_tree_node *root,
|
|
||||||
struct ecma119_tree_node *cur)
|
|
||||||
{
|
|
||||||
size_t max_path;
|
|
||||||
|
|
||||||
assert(root && cur && cur->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
cur->dir.depth = cur->parent ? cur->parent->dir.depth + 1 : 1;
|
|
||||||
cur->dir.path_len = cur->parent ? cur->parent->dir.path_len
|
|
||||||
+ strlen(cur->name) : 0;
|
|
||||||
max_path = cur->dir.path_len + cur->dir.depth + max_child_name_len(cur);
|
|
||||||
|
|
||||||
if (cur->dir.depth > 8 || max_path > 255) {
|
|
||||||
reparent(cur, root);
|
|
||||||
/* we are appended to the root's children now, so there is no
|
|
||||||
* need to recurse (the root will hit us again) */
|
|
||||||
} else {
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i=0; i < cur->dir.nchildren; i++) {
|
|
||||||
if (cur->dir.children[i]->type == ECMA119_DIR)
|
|
||||||
reorder_tree(root, cur->dir.children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cmp_node(const void *f1, const void *f2)
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *f = *((struct ecma119_tree_node**)f1);
|
|
||||||
struct ecma119_tree_node *g = *((struct ecma119_tree_node**)f2);
|
|
||||||
return strcmp(f->name, g->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sort_tree(struct ecma119_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(root && root->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
qsort(root->dir.children, root->dir.nchildren, sizeof(void*), cmp_node);
|
|
||||||
for (i=0; i < root->dir.nchildren; i++) {
|
|
||||||
if (root->dir.children[i]->type == ECMA119_DIR)
|
|
||||||
sort_tree(root->dir.children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change num_change characters of the given filename in order to ensure the
|
|
||||||
* name is unique. If the name is short enough (depending on the ISO level),
|
|
||||||
* we can append the characters instead of changing them.
|
|
||||||
*
|
|
||||||
* \p seq_num is the index of this file in the sequence of identical filenames.
|
|
||||||
*
|
|
||||||
* For example, seq_num=3, num_change=2, name="HELLOTHERE.TXT" changes name to
|
|
||||||
* "HELLOTHE03.TXT"
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
mangle_name(char **name, int num_change, int level, int seq_num)
|
|
||||||
{
|
|
||||||
char *dot = strrchr(*name, '.');
|
|
||||||
char *semi = strrchr(*name, ';');
|
|
||||||
size_t len = strlen(*name);
|
|
||||||
char base[len+1], ext[len+1];
|
|
||||||
char fmt[12];
|
|
||||||
size_t baselen, extlen;
|
|
||||||
|
|
||||||
if (num_change >= len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
strncpy(base, *name, len+1);
|
|
||||||
if (dot) {
|
|
||||||
base[dot - *name] = '\0';
|
|
||||||
strncpy(ext, dot+1, len+1);
|
|
||||||
if (semi) {
|
|
||||||
ext[semi - dot - 1] = '\0';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
base[len-2] = '\0';
|
|
||||||
ext[0] = '\0';
|
|
||||||
}
|
|
||||||
baselen = strlen(base);
|
|
||||||
extlen = strlen(ext);
|
|
||||||
if (level == 1 && baselen + num_change > 8) {
|
|
||||||
base[8 - num_change] = '\0';
|
|
||||||
} else if (level != 1 && baselen + extlen + num_change > 30) {
|
|
||||||
base[30 - extlen - num_change] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(fmt, "%%s%%0%1dd.%%s;1", num_change);
|
|
||||||
*name = realloc(*name, baselen + extlen + num_change + 4);
|
|
||||||
sprintf(*name, fmt, base, seq_num, ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mangle_all(struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
size_t i, j, k;
|
|
||||||
struct ecma119_dir_info d = dir->dir;
|
|
||||||
size_t n_change;
|
|
||||||
int changed;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
do {
|
|
||||||
changed = 0;
|
|
||||||
for (i=0; i < d.nchildren; i++) {
|
|
||||||
/* find the number of consecutive equal names */
|
|
||||||
j = 1;
|
|
||||||
while ( i+j < d.nchildren &&
|
|
||||||
!strcmp(d.children[i]->name,
|
|
||||||
d.children[i+j]->name) )
|
|
||||||
j++;
|
|
||||||
if (j == 1) continue;
|
|
||||||
|
|
||||||
/* mangle the names */
|
|
||||||
changed = 1;
|
|
||||||
n_change = j / 10 + 1;
|
|
||||||
for (k=0; k < j; k++) {
|
|
||||||
mangle_name(&(d.children[i+k]->name),
|
|
||||||
n_change,
|
|
||||||
dir->target->iso_level,
|
|
||||||
k);
|
|
||||||
d.children[i+k]->dirent_len =
|
|
||||||
calc_dirent_len(d.children[i+k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* skip ahead by the number of mangled names */
|
|
||||||
i += j - 1;
|
|
||||||
}
|
|
||||||
} while (changed);
|
|
||||||
|
|
||||||
for (i=0; i < d.nchildren; i++) {
|
|
||||||
if (d.children[i]->type == ECMA119_DIR)
|
|
||||||
mangle_all(d.children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ecma119_tree_node*
|
|
||||||
ecma119_tree_create(struct ecma119_write_target *t,
|
|
||||||
struct iso_tree_node *iso_root)
|
|
||||||
{
|
|
||||||
t->root = create_tree(t, NULL, iso_root);
|
|
||||||
reorder_tree(t->root, t->root);
|
|
||||||
sort_tree(t->root);
|
|
||||||
mangle_all(t->root);
|
|
||||||
return t->root;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ecma119_tree_print(struct ecma119_tree_node *root, int spaces)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
char sp[spaces+1];
|
|
||||||
|
|
||||||
memset(sp, ' ', spaces);
|
|
||||||
sp[spaces] = '\0';
|
|
||||||
|
|
||||||
printf("%s%s\n", sp, root->name);
|
|
||||||
if (root->type == ECMA119_DIR)
|
|
||||||
for (i=0; i < root->dir.nchildren; i++)
|
|
||||||
ecma119_tree_print(root->dir.children[i], spaces+2);
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \file ecma119_tree.h
|
|
||||||
*
|
|
||||||
* Declarations for creating, modifying and printing filesystem trees that
|
|
||||||
* are compatible with ecma119.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIBISO_ECMA119_TREE_H
|
|
||||||
#define LIBISO_ECMA119_TREE_H
|
|
||||||
|
|
||||||
struct ecma119_write_target;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ECMA119_FILE,
|
|
||||||
ECMA119_DIR
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_dir_info {
|
|
||||||
struct susp_info self_susp; /**< susp entries for "." */
|
|
||||||
struct susp_info parent_susp; /**< susp entries for ".." */
|
|
||||||
|
|
||||||
size_t len; /**< sum of the lengths of children's
|
|
||||||
* Directory Records (including SU) */
|
|
||||||
size_t CE_len; /**< sum of the lengths of children's
|
|
||||||
* SUSP CE areas */
|
|
||||||
|
|
||||||
int depth;
|
|
||||||
size_t path_len; /**< The length of a path up to, and
|
|
||||||
* including, this directory. This
|
|
||||||
* cannot exceed 255. */
|
|
||||||
size_t nchildren;
|
|
||||||
struct ecma119_tree_node **children;
|
|
||||||
|
|
||||||
struct ecma119_tree_node *real_parent;
|
|
||||||
/**< The parent before relocation */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ecma119_file_info
|
|
||||||
{
|
|
||||||
struct ecma119_tree_node *real_me;
|
|
||||||
/**< If this is non-NULL, the file is
|
|
||||||
* a placeholder for a relocated
|
|
||||||
* directory and this field points to
|
|
||||||
* that relocated directory.
|
|
||||||
*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node for a tree containing all the information necessary for writing
|
|
||||||
* an ISO9660 volume.
|
|
||||||
*/
|
|
||||||
struct ecma119_tree_node
|
|
||||||
{
|
|
||||||
char *name; /**< in ASCII, conforming to the
|
|
||||||
* current ISO level. */
|
|
||||||
size_t dirent_len; /**< Length of the directory record,
|
|
||||||
* not including SU. */
|
|
||||||
size_t block;
|
|
||||||
|
|
||||||
struct ecma119_tree_node *parent;
|
|
||||||
struct iso_tree_node *iso_self;
|
|
||||||
struct ecma119_write_target *target;
|
|
||||||
|
|
||||||
struct susp_info susp;
|
|
||||||
|
|
||||||
int type; /**< file or directory */
|
|
||||||
/* union {*/
|
|
||||||
struct ecma119_dir_info dir;
|
|
||||||
struct ecma119_file_info file;
|
|
||||||
/* };*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new ecma119_tree that corresponds to the tree represented by
|
|
||||||
* \p iso_root.
|
|
||||||
*/
|
|
||||||
struct ecma119_tree_node*
|
|
||||||
ecma119_tree_create(struct ecma119_write_target *target,
|
|
||||||
struct iso_tree_node *iso_root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free an ecma119 tree.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ecma119_tree_free(struct ecma119_tree_node *root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print an ecma119 tree.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ecma119_tree_print(struct ecma119_tree_node *root, int spaces);
|
|
||||||
|
|
||||||
#endif /* LIBISO_ECMA119_TREE_H */
|
|
@ -1,42 +0,0 @@
|
|||||||
#include "hash.h"
|
|
||||||
#include "exclude.h"
|
|
||||||
|
|
||||||
static struct iso_hash_node *table[HASH_NODES]={0,};
|
|
||||||
static int num=0;
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_exclude_add_path(const char *path)
|
|
||||||
{
|
|
||||||
if (!path)
|
|
||||||
return;
|
|
||||||
|
|
||||||
num += iso_hash_insert(table, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_exclude_remove_path(const char *path)
|
|
||||||
{
|
|
||||||
if (!num || !path)
|
|
||||||
return;
|
|
||||||
|
|
||||||
num -= iso_hash_remove(table, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_exclude_empty(void)
|
|
||||||
{
|
|
||||||
if (!num)
|
|
||||||
return;
|
|
||||||
|
|
||||||
iso_hash_empty(table);
|
|
||||||
num=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
iso_exclude_lookup(const char *path)
|
|
||||||
{
|
|
||||||
if (!num || !path)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return iso_hash_lookup(table, path);
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#ifndef ISO_EXCLUDE_H
|
|
||||||
#define ISO_EXCLUDE_H
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a path to ignore when adding a directory recursively.
|
|
||||||
*
|
|
||||||
* \param path The path, on the local filesystem, of the file.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
iso_exclude_lookup(const char *path);
|
|
||||||
|
|
||||||
#endif /* ISO_EXCLUDE */
|
|
158
libisofs/hash.c
158
libisofs/hash.c
@ -1,158 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
#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 */
|
|
@ -1,379 +0,0 @@
|
|||||||
/* -*- 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 <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);
|
|
||||||
ret->dirent_len = 34 + (ret->name ? ucslen(ret->name) * 2 : 0);
|
|
||||||
ret->len = iso->attrib.st_size; /* for dirs, we'll change this */
|
|
||||||
ret->block = iso->block; /* only actually for files, not dirs */
|
|
||||||
ret->parent = parent;
|
|
||||||
ret->iso_self = iso;
|
|
||||||
ret->target = t;
|
|
||||||
ret->nchildren = iso->nchildren;
|
|
||||||
if (ret->nchildren)
|
|
||||||
ret->children = calloc(sizeof(void*), ret->nchildren);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct joliet_tree_node*
|
|
||||||
create_tree(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *parent,
|
|
||||||
struct iso_tree_node *iso_root)
|
|
||||||
{
|
|
||||||
struct joliet_tree_node *root = create_node(t, parent, iso_root);
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < root->nchildren; i++) {
|
|
||||||
struct iso_tree_node *iso_ch = iso_root->children[i];
|
|
||||||
if (ISO_ISDIR(iso_ch))
|
|
||||||
root->children[i] = create_tree(t, root, iso_ch);
|
|
||||||
else
|
|
||||||
root->children[i] = create_node(t, root, iso_ch);
|
|
||||||
}
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
cmp_node(const void *f1, const void *f2)
|
|
||||||
{
|
|
||||||
struct joliet_tree_node *f = *((struct joliet_tree_node**)f1);
|
|
||||||
struct joliet_tree_node *g = *((struct joliet_tree_node**)f2);
|
|
||||||
return ucscmp(f->name, g->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sort_tree(struct joliet_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(root && ISO_ISDIR(root->iso_self));
|
|
||||||
|
|
||||||
qsort(root->children, root->nchildren, sizeof(void*), cmp_node);
|
|
||||||
for (i = 0; i < root->nchildren; i++)
|
|
||||||
if (ISO_ISDIR(root->children[i]->iso_self))
|
|
||||||
sort_tree(root->children[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_prepare_path_tables(struct ecma119_write_target *t)
|
|
||||||
{
|
|
||||||
size_t cur, i, j;
|
|
||||||
|
|
||||||
t->pathlist_joliet[0] = t->joliet_root;
|
|
||||||
t->path_table_size_joliet = 10; /* root directory record */
|
|
||||||
cur = 1;
|
|
||||||
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
struct joliet_tree_node *dir = t->pathlist_joliet[i];
|
|
||||||
for (j = 0; j < dir->nchildren; j++) {
|
|
||||||
struct joliet_tree_node *ch = dir->children[j];
|
|
||||||
if (ISO_ISDIR(ch->iso_self)) {
|
|
||||||
size_t len = 8 + ucslen(ch->name)*2;
|
|
||||||
t->pathlist_joliet[cur++] = ch;
|
|
||||||
t->path_table_size_joliet += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the size of each directory.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_calc_dir_size(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
struct joliet_tree_node *ch;
|
|
||||||
|
|
||||||
assert(root && ISO_ISDIR(root->iso_self));
|
|
||||||
|
|
||||||
root->len = 68; /* for "." and ".." entries */
|
|
||||||
for (i = 0; i < root->nchildren; i++) {
|
|
||||||
ch = root->children[i];
|
|
||||||
root->len += ch->dirent_len;
|
|
||||||
if (ISO_ISDIR(ch->iso_self))
|
|
||||||
joliet_calc_dir_size(t, ch);
|
|
||||||
}
|
|
||||||
t->total_dir_size_joliet += round_up (root->len, t->block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the position of each directory. Also fill out t->dirlist_joliet.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_calc_dir_pos(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
struct joliet_tree_node *ch;
|
|
||||||
|
|
||||||
assert(root && ISO_ISDIR(root->iso_self));
|
|
||||||
|
|
||||||
root->block = t->curblock;
|
|
||||||
t->curblock += div_up(root->len, t->block_size);
|
|
||||||
|
|
||||||
t->dirlist_joliet[t->curfile++] = root;
|
|
||||||
for (i = 0; i < root->nchildren; i++) {
|
|
||||||
ch = root->children[i];
|
|
||||||
if (ISO_ISDIR(ch->iso_self))
|
|
||||||
joliet_calc_dir_pos(t, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset curfile when we're finished */
|
|
||||||
if (!root->parent)
|
|
||||||
t->curfile = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_update_file_pos(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *dir)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
assert(dir && ISO_ISDIR(dir->iso_self));
|
|
||||||
for (i = 0; i < dir->nchildren; i++) {
|
|
||||||
struct joliet_tree_node *ch;
|
|
||||||
ch = dir->children[i];
|
|
||||||
|
|
||||||
if (!ISO_ISDIR (ch->iso_self)) {
|
|
||||||
struct iso_tree_node *iso = ch->iso_self;
|
|
||||||
ch->block = iso->block;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
joliet_update_file_pos(t, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset curfile when we're finished */
|
|
||||||
if (!dir->parent)
|
|
||||||
t->curfile = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct joliet_tree_node*
|
|
||||||
joliet_tree_create(struct ecma119_write_target *t,
|
|
||||||
struct iso_tree_node *iso_root)
|
|
||||||
{
|
|
||||||
struct joliet_tree_node *root = create_tree(t, NULL, iso_root);
|
|
||||||
|
|
||||||
sort_tree(root);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ugh. this is mostly C&P */
|
|
||||||
static void
|
|
||||||
write_path_table(struct ecma119_write_target *t,
|
|
||||||
int l_type,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
void (*write_int)(uint8_t*, uint32_t, int) = l_type ?
|
|
||||||
iso_lsb : iso_msb;
|
|
||||||
|
|
||||||
size_t i;
|
|
||||||
struct ecma119_path_table_record *rec;
|
|
||||||
struct joliet_tree_node *dir;
|
|
||||||
int parent = 0;
|
|
||||||
|
|
||||||
assert (t->joliet);
|
|
||||||
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
dir = t->pathlist_joliet[i];
|
|
||||||
while ((i) && t->pathlist_joliet[parent] != dir->parent)
|
|
||||||
parent++;
|
|
||||||
assert(parent < i || i == 0);
|
|
||||||
|
|
||||||
rec = (struct ecma119_path_table_record*) buf;
|
|
||||||
rec->len_di[0] = dir->parent ?
|
|
||||||
(uint8_t) ucslen(dir->name) * 2 : 1;
|
|
||||||
rec->len_xa[0] = 0;
|
|
||||||
write_int(rec->block, dir->block, 4);
|
|
||||||
write_int(rec->parent, parent + 1, 2);
|
|
||||||
if (dir->parent)
|
|
||||||
memcpy(rec->dir_id, dir->name, rec->len_di[0]);
|
|
||||||
buf += 8 + rec->len_di[0] + (rec->len_di[0] % 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if file_id is >= 0, we use it instead of the filename. As a magic number,
|
|
||||||
* file_id == 3 means that we are writing the root directory record (in order
|
|
||||||
* to distinguish it from the "." entry in the root directory) */
|
|
||||||
static void
|
|
||||||
write_one_dir_record(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *node,
|
|
||||||
int file_id,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
uint8_t len_dr = (file_id >= 0) ? 34 : node->dirent_len;
|
|
||||||
uint8_t len_fi = (file_id >= 0) ? 1 : ucslen(node->name) * 2;
|
|
||||||
uint8_t f_id = (uint8_t) ((file_id == 3) ? 0 : file_id);
|
|
||||||
uint8_t *name = (file_id >= 0) ? &f_id : (uint8_t*)node->name;
|
|
||||||
struct ecma119_dir_record *rec = (struct ecma119_dir_record*)buf;
|
|
||||||
|
|
||||||
if (file_id == 1 && node->parent)
|
|
||||||
node = node->parent;
|
|
||||||
|
|
||||||
rec->len_dr[0] = len_dr;
|
|
||||||
iso_bb(rec->block, node->block, 4);
|
|
||||||
iso_bb(rec->length, node->len, 4);
|
|
||||||
iso_datetime_7(rec->recording_time, t->now);
|
|
||||||
rec->flags[0] = ISO_ISDIR(node->iso_self) ? 2 : 0;
|
|
||||||
iso_bb(rec->vol_seq_number, t->volnum + 1, 2);
|
|
||||||
rec->len_fi[0] = len_fi;
|
|
||||||
memcpy(rec->file_id, name, len_fi);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_l_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
write_path_table (t, 1, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_m_path_table(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
write_path_table (t, 0, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
struct ecma119_sup_vol_desc *vol = (struct ecma119_sup_vol_desc*)buf;
|
|
||||||
struct iso_volume *volume = t->volset->volume[t->volnum];
|
|
||||||
uint16_t *vol_id = str2ucs(volume->volume_id);
|
|
||||||
uint16_t *pub_id = str2ucs(volume->publisher_id);
|
|
||||||
uint16_t *data_id = str2ucs(volume->data_preparer_id);
|
|
||||||
uint16_t *volset_id = str2ucs(t->volset->volset_id);
|
|
||||||
int vol_id_len = MIN(32, ucslen(vol_id) * 2);
|
|
||||||
int pub_id_len = MIN(128, ucslen(pub_id) * 2);
|
|
||||||
int data_id_len = MIN(128, ucslen(data_id) * 2);
|
|
||||||
int volset_id_len = MIN(128, ucslen(volset_id) * 2);
|
|
||||||
|
|
||||||
vol->vol_desc_type[0] = 2;
|
|
||||||
memcpy(vol->std_identifier, "CD001", 5);
|
|
||||||
vol->vol_desc_version[0] = 1;
|
|
||||||
memcpy(vol->system_id, "SYSID", 5);
|
|
||||||
if (vol_id)
|
|
||||||
memcpy(vol->volume_id, vol_id, vol_id_len);
|
|
||||||
memcpy(vol->esc_sequences, "%/E", 3);
|
|
||||||
iso_bb(vol->vol_space_size, t->vol_space_size, 4);
|
|
||||||
iso_bb(vol->vol_set_size, t->volset->volset_size, 2);
|
|
||||||
iso_bb(vol->vol_seq_number, t->volnum + 1, 2);
|
|
||||||
iso_bb(vol->block_size, t->block_size, 2);
|
|
||||||
iso_bb(vol->path_table_size, t->path_table_size_joliet, 4);
|
|
||||||
iso_lsb(vol->l_path_table_pos, t->l_path_table_pos_joliet, 4);
|
|
||||||
iso_msb(vol->m_path_table_pos, t->m_path_table_pos_joliet, 4);
|
|
||||||
|
|
||||||
write_one_dir_record(t, t->joliet_root, 3, vol->root_dir_record);
|
|
||||||
|
|
||||||
memcpy(vol->vol_set_id, volset_id, volset_id_len);
|
|
||||||
memcpy(vol->publisher_id, pub_id, pub_id_len);
|
|
||||||
memcpy(vol->data_prep_id, data_id, data_id_len);
|
|
||||||
/*memcpy(vol->application_id, "APPID", app_id_len);*/
|
|
||||||
|
|
||||||
iso_datetime_17(vol->vol_creation_time, t->now);
|
|
||||||
iso_datetime_17(vol->vol_modification_time, t->now);
|
|
||||||
iso_datetime_17(vol->vol_effective_time, t->now);
|
|
||||||
vol->file_structure_version[0] = 1;
|
|
||||||
|
|
||||||
free(vol_id);
|
|
||||||
free(volset_id);
|
|
||||||
free(pub_id);
|
|
||||||
free(data_id);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_one_dir(struct ecma119_write_target *t,
|
|
||||||
struct joliet_tree_node *dir,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
uint8_t *orig_buf = buf;
|
|
||||||
|
|
||||||
assert(ISO_ISDIR (dir->iso_self));
|
|
||||||
/* write the "." and ".." entries first */
|
|
||||||
write_one_dir_record(t, dir, 0, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
|
|
||||||
write_one_dir_record(t, dir, 1, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
|
|
||||||
for (i = 0; i < dir->nchildren; i++) {
|
|
||||||
write_one_dir_record(t, dir->children[i], -1, buf);
|
|
||||||
buf += ((struct ecma119_dir_record*) buf)->len_dr[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
assert (buf - orig_buf == dir->len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_dirs(struct ecma119_write_target *t, uint8_t *buf)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
struct joliet_tree_node *dir;
|
|
||||||
|
|
||||||
assert (t->curblock == t->dirlist_joliet[0]->block);
|
|
||||||
for (i = 0; i < t->dirlist_len; i++) {
|
|
||||||
dir = t->dirlist_joliet[i];
|
|
||||||
write_one_dir(t, dir, buf);
|
|
||||||
buf += round_up(dir->len, t->block_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_sup_vol_desc(struct ecma119_write_target *t,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t,
|
|
||||||
write_sup_vol_desc,
|
|
||||||
2048,
|
|
||||||
buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_l_path_table(struct ecma119_write_target *t,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t,
|
|
||||||
write_l_path_table,
|
|
||||||
t->path_table_size_joliet,
|
|
||||||
buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_m_path_table(struct ecma119_write_target *t,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t,
|
|
||||||
write_m_path_table,
|
|
||||||
t->path_table_size_joliet,
|
|
||||||
buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_dir_records(struct ecma119_write_target *t,
|
|
||||||
uint8_t *buf)
|
|
||||||
{
|
|
||||||
ecma119_start_chunking(t,
|
|
||||||
write_dirs,
|
|
||||||
t->total_dir_size_joliet,
|
|
||||||
buf);
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
/* -*- 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;
|
|
||||||
|
|
||||||
struct joliet_tree_node
|
|
||||||
{
|
|
||||||
uint16_t *name; /**< In UCS-2BE. */
|
|
||||||
size_t dirent_len;
|
|
||||||
size_t len;
|
|
||||||
size_t block;
|
|
||||||
|
|
||||||
struct joliet_tree_node *parent;
|
|
||||||
struct iso_tree_node *iso_self;
|
|
||||||
struct ecma119_write_target *target;
|
|
||||||
|
|
||||||
struct joliet_tree_node **children;
|
|
||||||
size_t nchildren;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new joliet_tree that corresponds to the tree represented by
|
|
||||||
* \p iso_root.
|
|
||||||
*/
|
|
||||||
struct joliet_tree_node*
|
|
||||||
joliet_tree_create(struct ecma119_write_target *target,
|
|
||||||
struct iso_tree_node *iso_root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the size of each directory in the joliet heirarchy.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_calc_dir_size(struct ecma119_write_target *t, struct joliet_tree_node*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the position of each directory in the joliet heirarchy.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_calc_dir_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the position of each file in the joliet hierarchy (to be called
|
|
||||||
* AFTER the file positions in the iso tree have been set).
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_update_file_pos(struct ecma119_write_target *t, struct joliet_tree_node*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the size of the joliet path table and fill in the list of
|
|
||||||
* directories.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
joliet_prepare_path_tables(struct ecma119_write_target *t);
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_tree_free(struct joliet_tree_node *root);
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_sup_vol_desc(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_l_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_m_path_table(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
void
|
|
||||||
joliet_wr_dir_records(struct ecma119_write_target *t, uint8_t *buf);
|
|
||||||
|
|
||||||
#endif /* LIBISO_JOLIET_H */
|
|
@ -1,225 +0,0 @@
|
|||||||
/* -*- 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 <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;
|
|
||||||
|
|
||||||
enum ecma119_extension_flag {
|
|
||||||
ECMA119_ROCKRIDGE = (1<<0),
|
|
||||||
ECMA119_JOLIET = (1<<1)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new volume.
|
|
||||||
* The parameters can be set to NULL if you wish to set them later.
|
|
||||||
*/
|
|
||||||
struct iso_volume *iso_volume_new(const char *volume_id,
|
|
||||||
const char *publisher_id,
|
|
||||||
const char *data_preparer_id);
|
|
||||||
|
|
||||||
struct iso_volume *iso_volume_new_with_root(const char *volume_id,
|
|
||||||
const char *publisher_id,
|
|
||||||
const char *data_preparer_id,
|
|
||||||
struct iso_tree_node *root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a volume.
|
|
||||||
*/
|
|
||||||
void iso_volume_free(struct iso_volume *volume);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a set of data volumes.
|
|
||||||
*/
|
|
||||||
void iso_volset_free(struct iso_volset *volume);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the root directory for a volume.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_volume_get_root(const struct iso_volume *volume);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill in the volume identifier for a volume.
|
|
||||||
*/
|
|
||||||
void iso_volume_set_volume_id(struct iso_volume *volume,
|
|
||||||
const char *volume_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill in the publisher for a volume.
|
|
||||||
*/
|
|
||||||
void iso_volume_set_publisher_id(struct iso_volume *volume,
|
|
||||||
const char *publisher_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill in the data preparer for a volume.
|
|
||||||
*/
|
|
||||||
void iso_volume_set_data_preparer_id(struct iso_volume *volume,
|
|
||||||
const char *data_preparer_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locate a node by its path on disc.
|
|
||||||
*
|
|
||||||
* \param volume The volume to search in.
|
|
||||||
* \param path The path, in the image, of the file.
|
|
||||||
*
|
|
||||||
* \return The node found or NULL.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a file or a directory (recursively) to a volume by specifying its path on the volume.
|
|
||||||
*
|
|
||||||
* \param volume The volume to add the file to.
|
|
||||||
* \param disc_path The path on the disc at which to add the disc.
|
|
||||||
* \param path The path, on the local filesystem, of the file.
|
|
||||||
*
|
|
||||||
* \return The node for the file or NULL if the parent doesn't exists on the disc.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_volume_add_path(struct iso_volume *volume,
|
|
||||||
const char *disc_path,
|
|
||||||
const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new, empty directory on the volume.
|
|
||||||
*
|
|
||||||
* \param volume The volume to add the directory to.
|
|
||||||
* \param disc_path The path on the volume at which to add the directory.
|
|
||||||
*
|
|
||||||
* \return A pointer to the newly created directory.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_volume_add_new_dir(struct iso_volume *volume,
|
|
||||||
const char *disc_path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Volume Set consisting of only one volume.
|
|
||||||
* @param volume The first and only volume for the volset to contain.
|
|
||||||
* @param volset_id The Volume Set ID.
|
|
||||||
* @return A new iso_volset.
|
|
||||||
*/
|
|
||||||
struct iso_volset *iso_volset_new(struct iso_volume *volume,
|
|
||||||
const char *volset_id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a file to a directory.
|
|
||||||
*
|
|
||||||
* \param path The path, on the local filesystem, of the file.
|
|
||||||
*
|
|
||||||
* \pre \p parent is NULL or is a directory.
|
|
||||||
* \pre \p path is non-NULL and is a valid path to a non-directory on the local
|
|
||||||
* filesystem.
|
|
||||||
* \return An iso_tree_node whose path is \p path and whose parent is \p parent.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_add_node(struct iso_tree_node *parent,
|
|
||||||
const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively add an existing directory to the tree.
|
|
||||||
* Warning: when using this, you'll lose pointers to files or subdirectories.
|
|
||||||
* If you want to have pointers to all files and directories,
|
|
||||||
* use iso_tree_add_file and iso_tree_add_dir.
|
|
||||||
*
|
|
||||||
* \param path The path, on the local filesystem, of the directory to add.
|
|
||||||
*
|
|
||||||
* \pre \p parent is NULL or is a directory.
|
|
||||||
* \pre \p path is non-NULL and is a valid path to a directory on the local
|
|
||||||
* filesystem.
|
|
||||||
* \return a pointer to the newly created directory.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_radd_dir(struct iso_tree_node *parent,
|
|
||||||
const char *path);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the path of a file or directory to ignore when adding a directory recursively.
|
|
||||||
*
|
|
||||||
* \param path The path, on the local filesystem, of the file.
|
|
||||||
*/
|
|
||||||
void iso_exclude_add_path(const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a path that was set to be ignored when adding a directory recusively.
|
|
||||||
*
|
|
||||||
* \param path The path, on the local filesystem, of the file.
|
|
||||||
*/
|
|
||||||
void iso_exclude_remove_path(const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all paths that were set to be ignored when adding a directory recusively.
|
|
||||||
*/
|
|
||||||
void iso_exclude_empty(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new, empty directory on the volume.
|
|
||||||
*
|
|
||||||
* \pre \p parent is NULL or is a directory.
|
|
||||||
* \pre \p name is unique among the children and files belonging to \p parent.
|
|
||||||
* Also, it doesn't contain '/' characters.
|
|
||||||
*
|
|
||||||
* \post \p parent contains a child directory whose name is \p name and whose
|
|
||||||
* POSIX attributes are the same as \p parent's.
|
|
||||||
* \return a pointer to the newly created directory.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_add_new_dir(struct iso_tree_node *parent,
|
|
||||||
const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the name of a file (using the current locale).
|
|
||||||
*/
|
|
||||||
void iso_tree_node_set_name(struct iso_tree_node *file, const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively print a directory to stdout.
|
|
||||||
* \param spaces The initial number of spaces on the left. Set to 0 if you
|
|
||||||
* supply a root directory.
|
|
||||||
*/
|
|
||||||
void iso_tree_print(const struct iso_tree_node *root, int spaces);
|
|
||||||
|
|
||||||
/** Create a burn_source which can be used as a data source for a track
|
|
||||||
*
|
|
||||||
* The volume set used to create the libburn_source can _not_ be modified
|
|
||||||
* until the libburn_source is freed.
|
|
||||||
*
|
|
||||||
* \param volumeset The volume set from which you want to write
|
|
||||||
* \param volnum The volume in the set which you want to write (usually 0)
|
|
||||||
* \param level ISO level to write at.
|
|
||||||
* \param flags Which extensions to support.
|
|
||||||
*
|
|
||||||
* \pre \p volumeset is non-NULL
|
|
||||||
* \pre \p volnum is less than \p volset->volset_size.
|
|
||||||
* \return A burn_source to be used for the data source for a track
|
|
||||||
*/
|
|
||||||
struct burn_source* iso_source_new_ecma119 (struct iso_volset *volumeset,
|
|
||||||
int volnum,
|
|
||||||
int level,
|
|
||||||
int flags);
|
|
||||||
|
|
||||||
#endif /* LIBISO_LIBISOFS_H */
|
|
@ -1,300 +0,0 @@
|
|||||||
/* 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>
|
|
||||||
|
|
||||||
/* create a PX field from the permissions on the current node. */
|
|
||||||
uint8_t *rrip_make_PX(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *PX = malloc(44);
|
|
||||||
|
|
||||||
PX[0] = 'P';
|
|
||||||
PX[1] = 'X';
|
|
||||||
PX[2] = 44;
|
|
||||||
PX[3] = 1;
|
|
||||||
iso_bb(&PX[4], node->iso_self->attrib.st_mode, 4);
|
|
||||||
iso_bb(&PX[12], node->iso_self->attrib.st_nlink, 4);
|
|
||||||
iso_bb(&PX[20], node->iso_self->attrib.st_uid, 4);
|
|
||||||
iso_bb(&PX[28], node->iso_self->attrib.st_gid, 4);
|
|
||||||
iso_bb(&PX[36], node->iso_self->attrib.st_ino, 4);
|
|
||||||
return PX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See IEEE 1282 4.1.1 */
|
|
||||||
void rrip_add_PX(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
susp_append(t, &node->susp, rrip_make_PX(t, node));
|
|
||||||
if (node->type == ECMA119_DIR) {
|
|
||||||
susp_append(t, &node->dir.self_susp, rrip_make_PX(t, node));
|
|
||||||
susp_append(t, &node->dir.parent_susp, rrip_make_PX(t, node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rrip_add_PN(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *PN = malloc(20);
|
|
||||||
|
|
||||||
PN[0] = 'P';
|
|
||||||
PN[1] = 'N';
|
|
||||||
PN[2] = 20;
|
|
||||||
PN[3] = 1;
|
|
||||||
iso_bb(&PN[4], node->iso_self->attrib.st_dev >> 32, 4);
|
|
||||||
iso_bb(&PN[12], node->iso_self->attrib.st_dev & 0xffffffff, 4);
|
|
||||||
susp_append(t, &node->susp, PN);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rrip_SL_append_comp(int *n, uint8_t ***comps,
|
|
||||||
char *s, int size, char fl)
|
|
||||||
{
|
|
||||||
uint8_t *comp = malloc(size + 2);
|
|
||||||
|
|
||||||
(*n)++;
|
|
||||||
comp[0] = fl;
|
|
||||||
comp[1] = size;
|
|
||||||
*comps = realloc(*comps, (*n) * sizeof(void*));
|
|
||||||
(*comps)[(*n) - 1] = comp;
|
|
||||||
|
|
||||||
if (size) {
|
|
||||||
memcpy(&comp[2], s, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rrip_SL_add_component(char *prev, char *cur, int *n_comp,
|
|
||||||
uint8_t ***comps)
|
|
||||||
{
|
|
||||||
int size = cur - prev;
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size == 1 && prev[0] == '.') {
|
|
||||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (size == 2 && !strncmp(prev, "..", 2)) {
|
|
||||||
rrip_SL_append_comp(n_comp, comps, prev, 0, 1 << 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we can't make a component any bigger than 250 (is this really a
|
|
||||||
problem)? because then it won't fit inside the SL field */
|
|
||||||
while (size > 248) {
|
|
||||||
size -= 248;
|
|
||||||
rrip_SL_append_comp(n_comp, comps, prev, 248, 1 << 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
rrip_SL_append_comp(n_comp, comps, prev, size, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rrip_add_SL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
int ret, pathsize = 0;
|
|
||||||
char *path = NULL, *cur, *prev;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
uint8_t **comp = NULL;
|
|
||||||
int n_comp = 0;
|
|
||||||
int total_comp_len = 0;
|
|
||||||
int written = 0, pos;
|
|
||||||
|
|
||||||
uint8_t *SL;
|
|
||||||
|
|
||||||
do {
|
|
||||||
pathsize += 128;
|
|
||||||
path = realloc(path, pathsize);
|
|
||||||
/* FIXME: what if the file is not on the local fs? */
|
|
||||||
ret = readlink(node->iso_self->loc.path, path, pathsize);
|
|
||||||
} while (ret == pathsize);
|
|
||||||
if (ret == -1) {
|
|
||||||
fprintf(stderr, "Error: couldn't read symlink: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
path[ret] = '\0';
|
|
||||||
|
|
||||||
prev = path;
|
|
||||||
for (cur = strchr(path, '/'); cur && *cur; cur = strchr(cur, '/')) {
|
|
||||||
rrip_SL_add_component(prev, cur, &n_comp, &comp);
|
|
||||||
cur++;
|
|
||||||
prev = cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if there was no trailing '/', we need to add the last component. */
|
|
||||||
if (prev == path || prev != &path[ret - 1]) {
|
|
||||||
rrip_SL_add_component(prev, &path[ret], &n_comp, &comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < n_comp; i++) {
|
|
||||||
total_comp_len += comp[i][1] + 2;
|
|
||||||
if (total_comp_len > 250) {
|
|
||||||
total_comp_len -= comp[i][1] + 2;
|
|
||||||
SL = malloc(total_comp_len + 5);
|
|
||||||
SL[0] = 'S';
|
|
||||||
SL[1] = 'L';
|
|
||||||
SL[2] = total_comp_len + 5;
|
|
||||||
SL[3] = 1;
|
|
||||||
SL[4] = 1; /* CONTINUE */
|
|
||||||
pos = 5;
|
|
||||||
for (j = written; j < i; j++) {
|
|
||||||
memcpy(&SL[pos], comp[j], comp[j][2]);
|
|
||||||
pos += comp[j][2];
|
|
||||||
}
|
|
||||||
susp_append(t, &node->susp, SL);
|
|
||||||
written = i - 1;
|
|
||||||
total_comp_len = comp[i][1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SL = malloc(total_comp_len + 5);
|
|
||||||
SL[0] = 'S';
|
|
||||||
SL[1] = 'L';
|
|
||||||
SL[2] = total_comp_len + 5;
|
|
||||||
SL[3] = 1;
|
|
||||||
SL[4] = 0;
|
|
||||||
pos = 5;
|
|
||||||
|
|
||||||
for (j = written; j < n_comp; j++) {
|
|
||||||
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
|
|
||||||
pos += comp[j][1] + 2;
|
|
||||||
}
|
|
||||||
susp_append(t, &node->susp, SL);
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
/* free the components */
|
|
||||||
for (i = 0; i < n_comp; i++) {
|
|
||||||
free(comp[i]);
|
|
||||||
}
|
|
||||||
free(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rrip_add_NM_single(struct ecma119_write_target *t,
|
|
||||||
struct susp_info *susp,
|
|
||||||
char *name, int size, int flags)
|
|
||||||
{
|
|
||||||
uint8_t *NM = malloc(size + 5);
|
|
||||||
|
|
||||||
NM[0] = 'N';
|
|
||||||
NM[1] = 'M';
|
|
||||||
NM[2] = size + 5;
|
|
||||||
NM[3] = 1;
|
|
||||||
NM[4] = flags;
|
|
||||||
if (size) {
|
|
||||||
memcpy(&NM[5], name, size);
|
|
||||||
}
|
|
||||||
susp_append(t, susp, NM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
rrip_add_NM(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
char *name = iso_p_fileid(node->iso_self->name);
|
|
||||||
int len = name ? strlen(name) : 0;
|
|
||||||
char *pos = name;
|
|
||||||
|
|
||||||
if (!len)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (node->type == ECMA119_DIR) {
|
|
||||||
rrip_add_NM_single(t, &node->dir.self_susp, pos, 0, 1 << 1);
|
|
||||||
rrip_add_NM_single(t, &node->dir.parent_susp, pos, 0, 1 << 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (len > 250) {
|
|
||||||
rrip_add_NM_single(t, &node->susp, pos, 250, 1);
|
|
||||||
len -= 250;
|
|
||||||
pos += 250;
|
|
||||||
}
|
|
||||||
rrip_add_NM_single(t, &node->susp, pos, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rrip_add_CL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *CL = calloc(1, 12);
|
|
||||||
|
|
||||||
CL[0] = 'C';
|
|
||||||
CL[1] = 'L';
|
|
||||||
CL[2] = 12;
|
|
||||||
CL[3] = 1;
|
|
||||||
susp_append(t, &node->susp, CL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
rrip_add_PL(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *PL = calloc(1, 12);
|
|
||||||
|
|
||||||
PL[0] = 'P';
|
|
||||||
PL[1] = 'L';
|
|
||||||
PL[2] = 12;
|
|
||||||
PL[3] = 1;
|
|
||||||
susp_append(t, &node->dir.parent_susp, PL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
rrip_add_RE(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *RE = malloc(4);
|
|
||||||
|
|
||||||
RE[0] = 'R';
|
|
||||||
RE[1] = 'E';
|
|
||||||
RE[2] = 4;
|
|
||||||
RE[3] = 1;
|
|
||||||
susp_append(t, &node->susp, RE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
rrip_add_TF(struct ecma119_write_target *t, struct ecma119_tree_node *node)
|
|
||||||
{
|
|
||||||
uint8_t *TF = malloc(5 + 3 * 7);
|
|
||||||
|
|
||||||
TF[0] = 'T';
|
|
||||||
TF[1] = 'F';
|
|
||||||
TF[2] = 5 + 3 * 7;
|
|
||||||
TF[3] = 1;
|
|
||||||
TF[4] = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 7);
|
|
||||||
iso_datetime_7(&TF[5], node->iso_self->attrib.st_mtime);
|
|
||||||
iso_datetime_7(&TF[12], node->iso_self->attrib.st_atime);
|
|
||||||
iso_datetime_7(&TF[19], node->iso_self->attrib.st_ctime);
|
|
||||||
susp_append(t, &node->susp, TF);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
rrip_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
if (dir->parent != dir->dir.real_parent) {
|
|
||||||
uint8_t *PL = susp_find(&dir->dir.parent_susp, "PL");
|
|
||||||
|
|
||||||
assert(PL);
|
|
||||||
iso_bb(&PL[4], dir->dir.real_parent->block, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
|
|
||||||
if (ch->type == ECMA119_FILE && ch->file.real_me) {
|
|
||||||
uint8_t *CL = susp_find(&ch->susp, "CL");
|
|
||||||
|
|
||||||
assert(CL);
|
|
||||||
iso_bb(&CL[4], ch->file.real_me->block, 4);
|
|
||||||
} else if (ch->type == ECMA119_DIR) {
|
|
||||||
rrip_finalize(t, ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
|
||||||
|
|
||||||
/** Functions and structures used for Rock Ridge support. */
|
|
||||||
|
|
||||||
#ifndef ISO_ROCKRIDGE_H
|
|
||||||
#define ISO_ROCKRIDGE_H
|
|
||||||
|
|
||||||
struct ecma119_write_target;
|
|
||||||
struct ecma119_tree_node;
|
|
||||||
|
|
||||||
void rrip_add_PX(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_PN(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_SL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_NM(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_CL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_RE(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_TF(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
/* This is special because it doesn't modify the susp fields of the directory
|
|
||||||
* that gets passed to it; it modifies the susp fields of the ".." entry in
|
|
||||||
* that directory. */
|
|
||||||
void rrip_add_PL(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
void rrip_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
#endif /* ISO_ROCKRIDGE_H */
|
|
280
libisofs/susp.c
280
libisofs/susp.c
@ -1,280 +0,0 @@
|
|||||||
/* 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->dir.self_susp, 34);
|
|
||||||
try_add_CE(t, &node->dir.parent_susp, 34);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See IEEE P1281 Draft Version 1.12/5.3 */
|
|
||||||
void
|
|
||||||
susp_add_SP(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
unsigned char *SP = malloc(7);
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
SP[0] = 'S';
|
|
||||||
SP[1] = 'P';
|
|
||||||
SP[2] = (char)7;
|
|
||||||
SP[3] = (char)1;
|
|
||||||
SP[4] = 0xbe;
|
|
||||||
SP[5] = 0xef;
|
|
||||||
SP[6] = 0;
|
|
||||||
susp_append(t, &dir->dir.self_susp, SP);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/** See IEEE P1281 Draft Version 1.12/5.4 */
|
|
||||||
static void susp_add_ST(struct ecma119_write_target *t,
|
|
||||||
struct iso_tree_node *node)
|
|
||||||
{
|
|
||||||
unsigned char *ST = malloc(4);
|
|
||||||
|
|
||||||
ST[0] = 'S';
|
|
||||||
ST[1] = 'T';
|
|
||||||
ST[2] = (char)4;
|
|
||||||
ST[3] = (char)1;
|
|
||||||
susp_append(t, node, ST);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** See IEEE P1281 Draft Version 1.12/5.5 FIXME: this is rockridge */
|
|
||||||
void
|
|
||||||
rrip_add_ER(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
unsigned char *ER = malloc(182);
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
ER[0] = 'E';
|
|
||||||
ER[1] = 'R';
|
|
||||||
ER[2] = 182;
|
|
||||||
ER[3] = 1;
|
|
||||||
ER[4] = 9;
|
|
||||||
ER[5] = 72;
|
|
||||||
ER[6] = 93;
|
|
||||||
ER[7] = 1;
|
|
||||||
memcpy(&ER[8], "IEEE_1282", 9);
|
|
||||||
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
|
|
||||||
"FILE SYSTEM SEMANTICS.", 72);
|
|
||||||
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
|
|
||||||
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
|
|
||||||
susp_append(t, &dir->dir.self_susp, ER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate the location of the CE areas. Since CE areas don't need to be
|
|
||||||
* aligned to a block boundary, we contatenate all CE areas from a single
|
|
||||||
* directory and dump them immediately after all the directory records.
|
|
||||||
*
|
|
||||||
* Requires that the following be known:
|
|
||||||
* - position of the current directory (dir->block)
|
|
||||||
* - length of the current directory (dir->dir.len)
|
|
||||||
* - sum of the children's CE lengths (dir->dir.CE_len)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
susp_fin_1_CE(struct ecma119_write_target *t,
|
|
||||||
struct susp_info *susp,
|
|
||||||
size_t block,
|
|
||||||
size_t *offset)
|
|
||||||
{
|
|
||||||
uint8_t *CE = susp->susp_fields[susp->n_fields_fit - 1];
|
|
||||||
|
|
||||||
if (!susp->CE_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
iso_bb(&CE[4], block + (*offset) / t->block_size, 4);
|
|
||||||
iso_bb(&CE[12], (*offset) % t->block_size, 4);
|
|
||||||
*offset += susp->CE_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void susp_fin_CE(struct ecma119_write_target *t,
|
|
||||||
struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
size_t CE_offset = dir->dir.len;
|
|
||||||
|
|
||||||
assert(dir->type == ECMA119_DIR);
|
|
||||||
|
|
||||||
susp_fin_1_CE(t, &dir->dir.self_susp, dir->block, &CE_offset);
|
|
||||||
susp_fin_1_CE(t, &dir->dir.parent_susp, dir->block, &CE_offset);
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
struct ecma119_tree_node *ch = dir->dir.children[i];
|
|
||||||
susp_fin_1_CE(t, &ch->susp, dir->block, &CE_offset);
|
|
||||||
}
|
|
||||||
assert(CE_offset == dir->dir.len + dir->dir.CE_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
susp_finalize(struct ecma119_write_target *t, struct ecma119_tree_node *dir)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(dir->type = ECMA119_DIR);
|
|
||||||
|
|
||||||
if (dir->dir.depth != 1) {
|
|
||||||
susp_fin_CE(t, dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < dir->dir.nchildren; i++) {
|
|
||||||
if (dir->dir.children[i]->type == ECMA119_DIR)
|
|
||||||
susp_finalize(t, dir->dir.children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void susp_write(struct ecma119_write_target *t,
|
|
||||||
struct susp_info *susp,
|
|
||||||
unsigned char *buf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < susp->n_fields_fit; i++) {
|
|
||||||
memcpy(&buf[pos], susp->susp_fields[i],
|
|
||||||
susp->susp_fields[i][2]);
|
|
||||||
pos += susp->susp_fields[i][2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void susp_write_CE(struct ecma119_write_target *t, struct susp_info *susp,
|
|
||||||
unsigned char *buf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (i = susp->n_fields_fit; i < susp->n_susp_fields; i++) {
|
|
||||||
memcpy(&buf[pos], susp->susp_fields[i],
|
|
||||||
susp->susp_fields[i][2]);
|
|
||||||
pos += susp->susp_fields[i][2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void susp_free_fields(struct susp_info *susp)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i=0; 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));
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/* vim: set noet ts=8 sts=8 sw=8 : */
|
|
||||||
|
|
||||||
/** Functions and structures used for SUSP (IEEE 1281).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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. */
|
|
||||||
};
|
|
||||||
|
|
||||||
void susp_add_CE(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
/* these next 2 are special because they don't modify the susp fields of the
|
|
||||||
* directory; they modify the susp fields of the
|
|
||||||
* "." entry in the directory. */
|
|
||||||
void susp_add_SP(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
void rrip_add_ER(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
/** Once all the directories and files are laid out, recurse through the tree
|
|
||||||
* and finalize all SUSP CE entries. */
|
|
||||||
void susp_finalize(struct ecma119_write_target *, struct ecma119_tree_node *);
|
|
||||||
|
|
||||||
void susp_append(struct ecma119_write_target *,
|
|
||||||
struct susp_info *,
|
|
||||||
uint8_t *);
|
|
||||||
void susp_insert(struct ecma119_write_target *,
|
|
||||||
struct susp_info *,
|
|
||||||
uint8_t *,
|
|
||||||
int pos);
|
|
||||||
uint8_t *susp_find(struct susp_info *,
|
|
||||||
const char *);
|
|
||||||
|
|
||||||
void susp_write(struct ecma119_write_target *,
|
|
||||||
struct susp_info *,
|
|
||||||
uint8_t *);
|
|
||||||
void susp_write_CE(struct ecma119_write_target *,
|
|
||||||
struct susp_info *,
|
|
||||||
uint8_t *);
|
|
||||||
|
|
||||||
void susp_free_fields(struct susp_info *);
|
|
||||||
|
|
||||||
#endif /* __ISO_SUSP */
|
|
223
libisofs/tree.c
223
libisofs/tree.c
@ -1,223 +0,0 @@
|
|||||||
/* 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 "util.h"
|
|
||||||
#include "volume.h"
|
|
||||||
#include "exclude.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_default_stat(struct stat *s)
|
|
||||||
{
|
|
||||||
time_t now = time(NULL);
|
|
||||||
|
|
||||||
memset(s, 0, sizeof(struct stat));
|
|
||||||
s->st_mode = 0777 | S_IFREG;
|
|
||||||
s->st_atime = s->st_mtime = s->st_ctime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct stat
|
|
||||||
get_attrib(const struct iso_tree_node *node)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
return node->attrib;
|
|
||||||
}
|
|
||||||
set_default_stat(&st);
|
|
||||||
return st;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
append_node(struct iso_tree_node *parent,
|
|
||||||
struct iso_tree_node *child)
|
|
||||||
{
|
|
||||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && child);
|
|
||||||
if (!parent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
parent->nchildren++;
|
|
||||||
parent->children =
|
|
||||||
realloc(parent->children, parent->nchildren * sizeof(void*));
|
|
||||||
parent->children[parent->nchildren-1] = child;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node*
|
|
||||||
iso_tree_new_root(struct iso_volume *vol)
|
|
||||||
{
|
|
||||||
assert(vol);
|
|
||||||
|
|
||||||
if (vol->root) {
|
|
||||||
iso_tree_free(vol->root);
|
|
||||||
}
|
|
||||||
|
|
||||||
vol->root = calloc(1, sizeof(struct iso_tree_node));
|
|
||||||
vol->root->volume = vol;
|
|
||||||
set_default_stat(&vol->root->attrib);
|
|
||||||
vol->root->attrib.st_mode = S_IFDIR | 0777;
|
|
||||||
vol->root->loc.type = LIBISO_NONE;
|
|
||||||
return vol->root;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node*
|
|
||||||
iso_tree_add_new_file(struct iso_tree_node *parent, const char *name)
|
|
||||||
{
|
|
||||||
struct iso_tree_node *f = calloc(1, sizeof(struct iso_tree_node));
|
|
||||||
|
|
||||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
|
|
||||||
|
|
||||||
f->volume = parent ? parent->volume : NULL;
|
|
||||||
f->parent = parent;
|
|
||||||
f->name = parent ? strdup(name) : NULL;
|
|
||||||
f->attrib = get_attrib(parent);
|
|
||||||
f->attrib.st_mode = 0777 | S_IFREG;
|
|
||||||
f->loc.type = LIBISO_NONE;
|
|
||||||
append_node(parent, f);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node*
|
|
||||||
iso_tree_add_new_dir(struct iso_tree_node *parent, const char *name)
|
|
||||||
{
|
|
||||||
struct iso_tree_node *d = iso_tree_add_new_file(parent, name);
|
|
||||||
|
|
||||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && name);
|
|
||||||
|
|
||||||
d->attrib.st_mode = (d->attrib.st_mode & ~S_IFMT) | S_IFDIR;
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node*
|
|
||||||
iso_tree_add_node(struct iso_tree_node *parent, const char *path)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
struct stat st;
|
|
||||||
struct iso_tree_node *ret;
|
|
||||||
|
|
||||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
|
|
||||||
|
|
||||||
if (lstat(path, &st) == -1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
p = strdup(path); /* because basename() might modify its arg */
|
|
||||||
|
|
||||||
/* it doesn't matter if we add a file or directory since we modify
|
|
||||||
* attrib anyway. */
|
|
||||||
ret = iso_tree_add_new_file(parent, basename(p));
|
|
||||||
ret->attrib = st;
|
|
||||||
ret->loc.type = LIBISO_FILESYS;
|
|
||||||
ret->loc.path = strdup(path);
|
|
||||||
free(p);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node*
|
|
||||||
iso_tree_radd_dir (struct iso_tree_node *parent, const char *path)
|
|
||||||
{
|
|
||||||
struct iso_tree_node *new;
|
|
||||||
DIR *dir;
|
|
||||||
struct dirent *ent;
|
|
||||||
|
|
||||||
assert((!parent || S_ISDIR(parent->attrib.st_mode)) && path);
|
|
||||||
|
|
||||||
new = iso_tree_add_node(parent, path);
|
|
||||||
if (!new || !S_ISDIR(new->attrib.st_mode)) {
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir = opendir(path);
|
|
||||||
if (!dir) {
|
|
||||||
warn("couldn't open directory %s: %s\n", path, strerror(errno));
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = readdir(dir))) {
|
|
||||||
char child[strlen(ent->d_name) + strlen(path) + 2];
|
|
||||||
|
|
||||||
if (strcmp(ent->d_name, ".") == 0 ||
|
|
||||||
strcmp(ent->d_name, "..") == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sprintf(child, "%s/%s", path, ent->d_name);
|
|
||||||
|
|
||||||
/* see if this child is excluded. */
|
|
||||||
if (iso_exclude_lookup(child))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
iso_tree_radd_dir(new, child);
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_tree_free(struct iso_tree_node *root)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i=0; i < root->nchildren; i++) {
|
|
||||||
iso_tree_free(root->children[i]);
|
|
||||||
}
|
|
||||||
free(root->name);
|
|
||||||
free(root->children);
|
|
||||||
free(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_tree_print(const struct iso_tree_node *root, int spaces)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
char sp[spaces+1];
|
|
||||||
|
|
||||||
memset(sp, ' ', spaces);
|
|
||||||
sp[spaces] = '\0';
|
|
||||||
|
|
||||||
printf("%s%sn", sp, root->name);
|
|
||||||
for (i=0; i < root->nchildren; i++) {
|
|
||||||
iso_tree_print(root->children[i], spaces+2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_tree_print_verbose(const struct iso_tree_node *root,
|
|
||||||
print_dir_callback dir,
|
|
||||||
print_file_callback file,
|
|
||||||
void *callback_data,
|
|
||||||
int spaces)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
(S_ISDIR(root->attrib.st_mode) ? dir : file)
|
|
||||||
(root, callback_data, spaces);
|
|
||||||
for (i=0; i < root->nchildren; i++) {
|
|
||||||
iso_tree_print_verbose(root->children[i], dir,
|
|
||||||
file, callback_data, spaces+2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_tree_node_set_name(struct iso_tree_node *file, const char *name)
|
|
||||||
{
|
|
||||||
free(file->name);
|
|
||||||
file->name = strdup(name);
|
|
||||||
}
|
|
159
libisofs/tree.h
159
libisofs/tree.h
@ -1,159 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
/* };*/
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A node in the filesystem tree.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node
|
|
||||||
{
|
|
||||||
struct iso_volume *volume;
|
|
||||||
struct iso_tree_node *parent;
|
|
||||||
char *name;
|
|
||||||
struct stat attrib; /**< The POSIX attributes of this node as
|
|
||||||
* documented in "man 2 stat". */
|
|
||||||
struct iso_file_location loc;
|
|
||||||
/**< Only used for regular files and symbolic
|
|
||||||
* links (ie. files for which we might have to
|
|
||||||
* copy data). */
|
|
||||||
|
|
||||||
size_t nchildren; /**< The number of children of this
|
|
||||||
* directory (if this is a directory). */
|
|
||||||
struct iso_tree_node **children;
|
|
||||||
|
|
||||||
size_t block; /**< The block at which this file will
|
|
||||||
* reside on disk. We store this here as
|
|
||||||
* well as in the various mangled trees
|
|
||||||
* because many different trees might point
|
|
||||||
* to the same file and they need to share the
|
|
||||||
* block location. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new root directory for a volume.
|
|
||||||
*
|
|
||||||
* \param vol The volume for which to create a new root directory.
|
|
||||||
*
|
|
||||||
* \pre \p vol is non-NULL.
|
|
||||||
* \post \p vol has a non-NULL, empty root directory with permissions 777.
|
|
||||||
* \return \p vol's new non-NULL, empty root directory.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_new_root(struct iso_volume *vol);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new, empty, file.
|
|
||||||
*
|
|
||||||
* \param parent The parent directory of the new file. If this is null, create
|
|
||||||
* and return a new file node without adding it to any tree.
|
|
||||||
* \param name The name of the new file, encoded in the current locale.
|
|
||||||
* \pre \p name is non-NULL and it does not match any other file or directory
|
|
||||||
* name in \p parent.
|
|
||||||
* \post \p parent (if non-NULL) contains a file with the following properties:
|
|
||||||
* - the file's name is \p name (converted to wchar_t)
|
|
||||||
* - the file's POSIX permissions are the same as \p parent's
|
|
||||||
* - the file is a regular file
|
|
||||||
* - the file is empty
|
|
||||||
*
|
|
||||||
* \return \p parent's newly created file.
|
|
||||||
*/
|
|
||||||
struct iso_tree_node *iso_tree_add_new_file(struct iso_tree_node *parent,
|
|
||||||
const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively free a directory.
|
|
||||||
*
|
|
||||||
* \param root The root of the directory heirarchy to free.
|
|
||||||
*
|
|
||||||
* \pre \p root is non-NULL.
|
|
||||||
*/
|
|
||||||
void iso_tree_free(struct iso_tree_node *root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function that prints verbose information about a directory.
|
|
||||||
*
|
|
||||||
* \param dir The directory about which to print information.
|
|
||||||
* \param data Unspecified function-dependent data.
|
|
||||||
* \param spaces The number of spaces to prepend to the output.
|
|
||||||
*
|
|
||||||
* \see iso_tree_print_verbose
|
|
||||||
*/
|
|
||||||
typedef void (*print_dir_callback) (const struct iso_tree_node *dir,
|
|
||||||
void *data,
|
|
||||||
int spaces);
|
|
||||||
/**
|
|
||||||
* A function that prints verbose information about a file.
|
|
||||||
*
|
|
||||||
* \param dir The file about which to print information.
|
|
||||||
* \param data Unspecified function-dependent data.
|
|
||||||
* \param spaces The number of spaces to prepend to the output.
|
|
||||||
*
|
|
||||||
* \see iso_tree_print_verbose
|
|
||||||
*/
|
|
||||||
typedef void (*print_file_callback) (const struct iso_tree_node *file,
|
|
||||||
void *data,
|
|
||||||
int spaces);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively print a directory heirarchy. For each node in the directory
|
|
||||||
* heirarchy, call a callback function to print information more verbosely.
|
|
||||||
*
|
|
||||||
* \param root The root of the directory heirarchy to print.
|
|
||||||
* \param dir The callback function to call for each directory in the tree.
|
|
||||||
* \param file The callback function to call for each file in the tree.
|
|
||||||
* \param callback_data The data to pass to the callback functions.
|
|
||||||
* \param spaces The number of spaces to prepend to the output.
|
|
||||||
*
|
|
||||||
* \pre \p root is not NULL.
|
|
||||||
* \pre Neither of the callback functions modifies the directory heirarchy.
|
|
||||||
*/
|
|
||||||
void iso_tree_print_verbose(const struct iso_tree_node *root,
|
|
||||||
print_dir_callback dir,
|
|
||||||
print_file_callback file,
|
|
||||||
void *callback_data,
|
|
||||||
int spaces);
|
|
||||||
|
|
||||||
#define ISO_ISDIR(n) S_ISDIR(n->attrib.st_mode)
|
|
||||||
|
|
||||||
#endif /* LIBISO_TREE_H */
|
|
577
libisofs/util.c
577
libisofs/util.c
@ -1,577 +0,0 @@
|
|||||||
/* -*- 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 "util.h"
|
|
||||||
|
|
||||||
/* avoids warning and names in iso, joliet and rockridge can't be > 255 bytes
|
|
||||||
* anyway. There are at most 31 characters in iso level 1, 255 for rockridge,
|
|
||||||
* 64 characters (* 2 since UCS) for joliet. */
|
|
||||||
#define NAME_BUFFER_SIZE 255
|
|
||||||
|
|
||||||
int div_up(int n, int div)
|
|
||||||
{
|
|
||||||
return (n + div - 1) / div;
|
|
||||||
}
|
|
||||||
|
|
||||||
int round_up(int n, int mul)
|
|
||||||
{
|
|
||||||
return div_up(n, mul) * mul;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this function must always return a name
|
|
||||||
* since the caller never checks if a NULL
|
|
||||||
* is returned. It also avoids some warnings. */
|
|
||||||
char *str2ascii(const char *src_arg)
|
|
||||||
{
|
|
||||||
wchar_t wsrc_[NAME_BUFFER_SIZE];
|
|
||||||
char *src = (char*)wsrc_;
|
|
||||||
char *ret_;
|
|
||||||
char *ret;
|
|
||||||
mbstate_t state;
|
|
||||||
iconv_t conv;
|
|
||||||
size_t numchars;
|
|
||||||
size_t outbytes;
|
|
||||||
size_t inbytes;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (!src_arg)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* convert the string to a wide character string. Note: outbytes
|
|
||||||
* is in fact the number of characters in the string and doesn't
|
|
||||||
* include the last NULL character. */
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
|
|
||||||
if (numchars < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
inbytes = numchars * sizeof(wchar_t);
|
|
||||||
|
|
||||||
ret_ = malloc(numchars+1);
|
|
||||||
outbytes = numchars;
|
|
||||||
ret = ret_;
|
|
||||||
|
|
||||||
/* initialize iconv */
|
|
||||||
conv = iconv_open("ASCII", "WCHAR_T");
|
|
||||||
if (conv == (iconv_t)-1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
|
||||||
while(n == -1) {
|
|
||||||
/* The destination buffer is too small. Stops here. */
|
|
||||||
if(errno == E2BIG)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* An incomplete multi bytes sequence was found. We
|
|
||||||
* can't do anything here. That's quite unlikely. */
|
|
||||||
if(errno == EINVAL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* The last possible error is an invalid multi bytes
|
|
||||||
* sequence. Just replace the character with a "_".
|
|
||||||
* Probably the character doesn't exist in ascii like
|
|
||||||
* "é, è, à, ç, ..." in French. */
|
|
||||||
*ret++ = '_';
|
|
||||||
outbytes--;
|
|
||||||
|
|
||||||
if(!outbytes)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* There was an error with one character but some other remain
|
|
||||||
* to be converted. That's probably a multibyte character.
|
|
||||||
* See above comment. */
|
|
||||||
src += sizeof(wchar_t);
|
|
||||||
inbytes -= sizeof(wchar_t);
|
|
||||||
|
|
||||||
if(!inbytes)
|
|
||||||
break;
|
|
||||||
|
|
||||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
iconv_close(conv);
|
|
||||||
|
|
||||||
*ret='\0';
|
|
||||||
return ret_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: C&P */
|
|
||||||
uint16_t *str2ucs(const char *src_arg)
|
|
||||||
{
|
|
||||||
wchar_t wsrc_[NAME_BUFFER_SIZE];
|
|
||||||
char *src = (char*)wsrc_;
|
|
||||||
char *ret_;
|
|
||||||
char *ret;
|
|
||||||
mbstate_t state;
|
|
||||||
iconv_t conv;
|
|
||||||
size_t outbytes;
|
|
||||||
size_t numchars;
|
|
||||||
size_t inbytes;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (!src_arg)
|
|
||||||
return calloc(2, 1); /* empty UCS string */
|
|
||||||
|
|
||||||
/* convert the string to a wide character string. Note: outbytes
|
|
||||||
* is in fact the number of characters in the string and doesn't
|
|
||||||
* include the last NULL character. */
|
|
||||||
memset(&state, 0, sizeof(state));
|
|
||||||
numchars = mbsrtowcs(wsrc_, &src_arg, NAME_BUFFER_SIZE-1, &state);
|
|
||||||
if (numchars < 0)
|
|
||||||
return calloc(2, 1); /* empty UCS string */
|
|
||||||
|
|
||||||
inbytes = numchars * sizeof(wchar_t);
|
|
||||||
|
|
||||||
outbytes = numchars * sizeof(uint16_t);
|
|
||||||
ret_ = malloc ((numchars+1) * sizeof(uint16_t));
|
|
||||||
ret = ret_;
|
|
||||||
|
|
||||||
/* initialize iconv */
|
|
||||||
conv = iconv_open("UCS-2BE", "WCHAR_T");
|
|
||||||
if (conv == (iconv_t)-1)
|
|
||||||
return calloc(2, 1); /* empty UCS string */
|
|
||||||
|
|
||||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
|
||||||
while(n == -1) {
|
|
||||||
/* The destination buffer is too small. Stops here. */
|
|
||||||
if(errno == E2BIG)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* An incomplete multi bytes sequence was found. We
|
|
||||||
* can't do anything here. That's quite unlikely. */
|
|
||||||
if(errno == EINVAL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* The last possible error is an invalid multi bytes
|
|
||||||
* sequence. Just replace the character with a "_".
|
|
||||||
* Probably the character doesn't exist in ascii like
|
|
||||||
* "é, è, à, ç, ..." in French. */
|
|
||||||
*((uint16_t*) ret) = '_';
|
|
||||||
ret += sizeof(uint16_t);
|
|
||||||
outbytes -= sizeof(uint16_t);
|
|
||||||
|
|
||||||
if(!outbytes)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* There was an error with one character but some other remain
|
|
||||||
* to be converted. That's probably a multibyte character.
|
|
||||||
* See above comment. */
|
|
||||||
src += sizeof(wchar_t);
|
|
||||||
inbytes -= sizeof(wchar_t);
|
|
||||||
|
|
||||||
if(!inbytes)
|
|
||||||
break;
|
|
||||||
|
|
||||||
n = iconv(conv, &src, &inbytes, &ret, &outbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
iconv_close(conv);
|
|
||||||
|
|
||||||
/* close the ucs string */
|
|
||||||
*((uint16_t*) ret) = 0;
|
|
||||||
|
|
||||||
return (uint16_t*)ret_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int valid_d_char(char c)
|
|
||||||
{
|
|
||||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
static int valid_a_char(char c)
|
|
||||||
{
|
|
||||||
return (c >= ' ' && c <= '"') || (c >= '%' && c <= '?')
|
|
||||||
|| (c >= 'A' && c <= 'Z')
|
|
||||||
|| (c == '_');
|
|
||||||
}
|
|
||||||
|
|
||||||
static int valid_j_char(uint16_t c)
|
|
||||||
{
|
|
||||||
return !(c < (uint16_t)' ' || c == (uint16_t)'*' || c == (uint16_t)'/'
|
|
||||||
|| c == (uint16_t)':' || c == (uint16_t)';'
|
|
||||||
|| c == (uint16_t)'?' || c == (uint16_t)'\\');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: where are these documented? */
|
|
||||||
static int valid_p_char(char c)
|
|
||||||
{
|
|
||||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
|
|
||||||
|| (c >= 'a' && c <= 'z')
|
|
||||||
|| (c == '.') || (c == '_') || (c == '-');
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *iso_dirid(const char *src, int size)
|
|
||||||
{
|
|
||||||
char *ret = str2ascii(src);
|
|
||||||
size_t len, i;
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
len = strlen(ret);
|
|
||||||
if (len > size) {
|
|
||||||
ret[size] = '\0';
|
|
||||||
len = size;
|
|
||||||
}
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
char c = toupper(ret[i]);
|
|
||||||
ret[i] = valid_d_char(c) ? c : '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *iso_1_dirid(const char *src)
|
|
||||||
{
|
|
||||||
return iso_dirid(src, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *iso_2_dirid(const char *src)
|
|
||||||
{
|
|
||||||
return iso_dirid(src, 31);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *iso_1_fileid(const char *src_arg)
|
|
||||||
{
|
|
||||||
char *src = str2ascii(src_arg);
|
|
||||||
char *dest;
|
|
||||||
char *dot; /* Position of the last dot in the
|
|
||||||
filename, will be used to calculate
|
|
||||||
lname and lext. */
|
|
||||||
int lname, lext, pos, i;
|
|
||||||
|
|
||||||
if (!src)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dest = malloc(15); /* 15 = 8 (name) + 1 (.) + 3 (ext) + 2
|
|
||||||
(;1) + 1 (\0) */
|
|
||||||
dot = strrchr(src, '.');
|
|
||||||
|
|
||||||
lext = dot ? strlen(dot + 1) : 0;
|
|
||||||
lname = strlen(src) - lext - (dot ? 1 : 0);
|
|
||||||
|
|
||||||
/* If we can't build a filename, return NULL. */
|
|
||||||
if (lname == 0 && lext == 0) {
|
|
||||||
free(src);
|
|
||||||
free(dest);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
/* Convert up to 8 characters of the filename. */
|
|
||||||
for (i = 0; i < lname && i < 8; i++) {
|
|
||||||
char c = toupper(src[i]);
|
|
||||||
|
|
||||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
|
||||||
}
|
|
||||||
/* This dot is mandatory, even if there is no extension. */
|
|
||||||
dest[pos++] = '.';
|
|
||||||
/* Convert up to 3 characters of the extension, if any. */
|
|
||||||
for (i = 0; i < lext && i < 3; i++) {
|
|
||||||
char c = toupper(src[lname + 1 + i]);
|
|
||||||
|
|
||||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
|
||||||
}
|
|
||||||
/* File versions are mandatory, even if they aren't used. */
|
|
||||||
dest[pos++] = ';';
|
|
||||||
dest[pos++] = '1';
|
|
||||||
dest[pos] = '\0';
|
|
||||||
dest = (char *)realloc(dest, pos + 1);
|
|
||||||
|
|
||||||
free(src);
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *iso_2_fileid(const char *src_arg)
|
|
||||||
{
|
|
||||||
char *src = str2ascii(src_arg);
|
|
||||||
char *dest;
|
|
||||||
char *dot;
|
|
||||||
int lname, lext, lnname, lnext, pos, i;
|
|
||||||
|
|
||||||
if (!src)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dest = malloc(34); /* 34 = 30 (name + ext) + 1 (.) + 2
|
|
||||||
(;1) + 1 (\0) */
|
|
||||||
dot = strrchr(src, '.');
|
|
||||||
|
|
||||||
/* Since the maximum length can be divided freely over the name and
|
|
||||||
extension, we need to calculate their new lengths (lnname and
|
|
||||||
lnext). If the original filename is too long, we start by trimming
|
|
||||||
the extension, but keep a minimum extension length of 3. */
|
|
||||||
if (dot == NULL || dot == src || *(dot + 1) == '\0') {
|
|
||||||
lname = strlen(src);
|
|
||||||
lnname = (lname > 30) ? 30 : lname;
|
|
||||||
lext = lnext = 0;
|
|
||||||
} else {
|
|
||||||
lext = strlen(dot + 1);
|
|
||||||
lname = strlen(src) - lext - 1;
|
|
||||||
lnext = (strlen(src) > 31 && lext > 3)
|
|
||||||
? (lname < 27 ? 30 - lname : 3) : lext;
|
|
||||||
lnname = (strlen(src) > 31) ? 30 - lnext : lname;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lnname == 0 && lnext == 0) {
|
|
||||||
free(src);
|
|
||||||
free(dest);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = 0;
|
|
||||||
/* Convert up to lnname characters of the filename. */
|
|
||||||
for (i = 0; i < lnname; i++) {
|
|
||||||
char c = toupper(src[i]);
|
|
||||||
|
|
||||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
|
||||||
}
|
|
||||||
dest[pos++] = '.';
|
|
||||||
/* Convert up to lnext characters of the extension, if any. */
|
|
||||||
for (i = 0; i < lnext; i++) {
|
|
||||||
char c = toupper(src[lname + 1 + i]);
|
|
||||||
|
|
||||||
dest[pos++] = valid_d_char(c) ? c : '_';
|
|
||||||
}
|
|
||||||
dest[pos++] = ';';
|
|
||||||
dest[pos++] = '1';
|
|
||||||
dest[pos] = '\0';
|
|
||||||
dest = (char *)realloc(dest, pos + 1);
|
|
||||||
|
|
||||||
free(src);
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
iso_p_fileid(const char *src)
|
|
||||||
{
|
|
||||||
char *ret = str2ascii(src);
|
|
||||||
size_t i, len;
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
|
||||||
len = strlen(ret);
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (!valid_p_char(ret[i])) {
|
|
||||||
ret[i] = (uint16_t)'_';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t *
|
|
||||||
iso_j_id(const char *src_arg)
|
|
||||||
{
|
|
||||||
uint16_t *j_str = str2ucs(src_arg);
|
|
||||||
size_t len = ucslen(j_str);
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (len > 128) {
|
|
||||||
j_str[128] = '\0';
|
|
||||||
len = 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (n = 0; n < len; n++)
|
|
||||||
if (!valid_j_char(j_str[n]))
|
|
||||||
j_str[n] = '_';
|
|
||||||
return j_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_lsb(uint8_t *buf, uint32_t num, int bytes)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(bytes <= 4);
|
|
||||||
|
|
||||||
for (i = 0; i < bytes; ++i)
|
|
||||||
buf[i] = (num >> (8 * i)) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_msb(uint8_t *buf, uint32_t num, int bytes)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
assert(bytes <= 4);
|
|
||||||
|
|
||||||
for (i = 0; i < bytes; ++i)
|
|
||||||
buf[bytes - 1 - i] = (num >> (8 * i)) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_bb(uint8_t *buf, uint32_t num, int bytes)
|
|
||||||
{
|
|
||||||
iso_lsb(buf, num, bytes);
|
|
||||||
iso_msb(buf+bytes, num, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void iso_datetime_7(unsigned char *buf, time_t t)
|
|
||||||
{
|
|
||||||
static int tzsetup = 0;
|
|
||||||
static int tzoffset;
|
|
||||||
struct tm tm;
|
|
||||||
|
|
||||||
if (!tzsetup) {
|
|
||||||
tzset();
|
|
||||||
|
|
||||||
tzoffset = -timezone / 60 / 15;
|
|
||||||
if (tzoffset < -48)
|
|
||||||
tzoffset += 101;
|
|
||||||
|
|
||||||
tzsetup = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
localtime_r(&t, &tm);
|
|
||||||
|
|
||||||
buf[0] = tm.tm_year;
|
|
||||||
buf[1] = tm.tm_mon + 1;
|
|
||||||
buf[2] = tm.tm_mday;
|
|
||||||
buf[3] = tm.tm_hour;
|
|
||||||
buf[4] = tm.tm_min;
|
|
||||||
buf[5] = tm.tm_sec;
|
|
||||||
buf[6] = tzoffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
time_t iso_datetime_read_7(const uint8_t *buf)
|
|
||||||
{
|
|
||||||
struct tm tm;
|
|
||||||
|
|
||||||
tm.tm_year = buf[0];
|
|
||||||
tm.tm_mon = buf[1] + 1;
|
|
||||||
tm.tm_mday = buf[2];
|
|
||||||
tm.tm_hour = buf[3];
|
|
||||||
tm.tm_min = buf[4];
|
|
||||||
tm.tm_sec = buf[5];
|
|
||||||
|
|
||||||
return mktime(&tm) - buf[6] * 60 * 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
void iso_datetime_17(unsigned char *buf, time_t t)
|
|
||||||
{
|
|
||||||
static int tzsetup = 0;
|
|
||||||
static int tzoffset;
|
|
||||||
struct tm tm;
|
|
||||||
|
|
||||||
if (t == (time_t) - 1) {
|
|
||||||
/* unspecified time */
|
|
||||||
memset(buf, '0', 16);
|
|
||||||
buf[16] = 0;
|
|
||||||
} else {
|
|
||||||
if (!tzsetup) {
|
|
||||||
tzset();
|
|
||||||
|
|
||||||
tzoffset = -timezone / 60 / 15;
|
|
||||||
if (tzoffset < -48)
|
|
||||||
tzoffset += 101;
|
|
||||||
|
|
||||||
tzsetup = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
localtime_r(&t, &tm);
|
|
||||||
|
|
||||||
sprintf((char*)&buf[0], "%04d", tm.tm_year + 1900);
|
|
||||||
sprintf((char*)&buf[4], "%02d", tm.tm_mon + 1);
|
|
||||||
sprintf((char*)&buf[6], "%02d", tm.tm_mday);
|
|
||||||
sprintf((char*)&buf[8], "%02d", tm.tm_hour);
|
|
||||||
sprintf((char*)&buf[10], "%02d", tm.tm_min);
|
|
||||||
sprintf((char*)&buf[12], "%02d", MIN(59, tm.tm_sec));
|
|
||||||
memcpy(&buf[14], "00", 2);
|
|
||||||
buf[16] = tzoffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
121
libisofs/util.h
121
libisofs/util.h
@ -1,121 +0,0 @@
|
|||||||
/* -*- 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t *towcs(const char *);
|
|
||||||
char *str2ascii(const char*);
|
|
||||||
uint16_t *str2ucs(const char*);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a level 1 directory identifier.
|
|
||||||
*/
|
|
||||||
char *iso_1_dirid(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a level 2 directory identifier.
|
|
||||||
*/
|
|
||||||
char *iso_2_dirid(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a level 1 file identifier that consists of a name, extension and
|
|
||||||
* version number. The resulting string will have a file name of maximum
|
|
||||||
* length 8, followed by a separator (.), an optional extension of maximum
|
|
||||||
* length 3, followed by a separator (;) and a version number (digit 1).
|
|
||||||
* @return NULL if the original name and extension both are of length 0.
|
|
||||||
*/
|
|
||||||
char *iso_1_fileid(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a level 2 file identifier that consists of a name, extension and
|
|
||||||
* version number. The combined file name and extension length will not exceed
|
|
||||||
* 30, the name and extension will be separated (.), and the extension will be
|
|
||||||
* followed by a separator (;) and a version number (digit 1).
|
|
||||||
* @return NULL if the original name and extension both are of length 0.
|
|
||||||
*/
|
|
||||||
char *iso_2_fileid(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Joliet file or directory identifier that consists of a name,
|
|
||||||
* extension and version number. The combined name and extension length will
|
|
||||||
* not exceed 128 bytes, the name and extension will be separated (.),
|
|
||||||
* and the extension will be followed by a separator (;) and a version number
|
|
||||||
* (digit 1). All characters consist of 2 bytes and the resulting string is
|
|
||||||
* NULL-terminated by a 2-byte NULL. Requires the locale to be set correctly.
|
|
||||||
*
|
|
||||||
* @param size will be set to the size (in bytes) of the identifier.
|
|
||||||
* @return NULL if the original name and extension both are of length 0 or the conversion from the current codeset to UCS-2BE is not available.
|
|
||||||
*/
|
|
||||||
uint16_t *iso_j_id(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FIXME: what are the requirements for these next two? Is this for RR?
|
|
||||||
*
|
|
||||||
* Create a POSIX portable file name that consists of a name and extension.
|
|
||||||
* The resulting file name will not exceed 250 characters.
|
|
||||||
* @return NULL if the original name and extension both are of length 0.
|
|
||||||
*/
|
|
||||||
char *iso_p_fileid(const char *src);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a POSIX portable directory name.
|
|
||||||
* The resulting directory name will not exceed 250 characters.
|
|
||||||
* @return NULL if the original name is of length 0.
|
|
||||||
*/
|
|
||||||
char *iso_p_dirid(const char *src);
|
|
||||||
|
|
||||||
void iso_lsb(uint8_t *buf, uint32_t num, int bytes);
|
|
||||||
void iso_msb(uint8_t *buf, uint32_t num, int bytes);
|
|
||||||
void iso_bb(uint8_t *buf, uint32_t num, int bytes);
|
|
||||||
|
|
||||||
uint32_t iso_read_lsb(const uint8_t *buf, int bytes);
|
|
||||||
uint32_t iso_read_msb(const uint8_t *buf, int bytes);
|
|
||||||
uint32_t iso_read_bb(const uint8_t *buf, int bytes);
|
|
||||||
|
|
||||||
/** Records the date/time into a 7 byte buffer (9.1.5) */
|
|
||||||
void iso_datetime_7(uint8_t *buf, time_t t);
|
|
||||||
|
|
||||||
/** Records the date/time into a 17 byte buffer (8.4.26.1) */
|
|
||||||
void iso_datetime_17(uint8_t *buf, time_t t);
|
|
||||||
|
|
||||||
time_t iso_datetime_read_7(const uint8_t *buf);
|
|
||||||
time_t iso_datetime_read_17(const uint8_t *buf);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like strlen, but for Joliet strings.
|
|
||||||
*/
|
|
||||||
size_t ucslen(const uint16_t *str);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like strcmp, but for Joliet strings.
|
|
||||||
*/
|
|
||||||
int ucscmp(const uint16_t *s1, const uint16_t *s2);
|
|
||||||
|
|
||||||
#endif /* LIBISO_UTIL_H */
|
|
@ -1,189 +0,0 @@
|
|||||||
/* -*- 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"
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
vol->refcount++;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_volume*
|
|
||||||
iso_volume_new(const char *volume_id,
|
|
||||||
const char *publisher_id,
|
|
||||||
const char *data_preparer_id)
|
|
||||||
{
|
|
||||||
return iso_volume_new_with_root(volume_id,
|
|
||||||
publisher_id,
|
|
||||||
data_preparer_id,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_volume*
|
|
||||||
iso_volume_new_with_root(const char *volume_id,
|
|
||||||
const char *publisher_id,
|
|
||||||
const char *data_preparer_id,
|
|
||||||
struct iso_tree_node *root)
|
|
||||||
{
|
|
||||||
struct iso_volume *volume;
|
|
||||||
|
|
||||||
volume = calloc(1, sizeof(struct iso_volume));
|
|
||||||
volume->refcount = 1;
|
|
||||||
|
|
||||||
volume->root = root ? root : iso_tree_new_root(volume);
|
|
||||||
|
|
||||||
if (volume_id != NULL)
|
|
||||||
volume->volume_id = strdup(volume_id);
|
|
||||||
if (publisher_id != NULL)
|
|
||||||
volume->publisher_id = strdup(publisher_id);
|
|
||||||
if (data_preparer_id != NULL)
|
|
||||||
volume->data_preparer_id = strdup(data_preparer_id);
|
|
||||||
return volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
iso_volume_free(struct iso_volume *volume)
|
|
||||||
{
|
|
||||||
/* Only free if no references are in use. */
|
|
||||||
if (--volume->refcount < 1) {
|
|
||||||
iso_tree_free(volume->root);
|
|
||||||
|
|
||||||
free(volume->volume_id);
|
|
||||||
free(volume->publisher_id);
|
|
||||||
free(volume->data_preparer_id);
|
|
||||||
|
|
||||||
free(volume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node *
|
|
||||||
iso_volume_get_root(const struct iso_volume *volume)
|
|
||||||
{
|
|
||||||
return volume->root;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node *
|
|
||||||
iso_tree_volume_path_to_node(struct iso_volume *volume, const char *path)
|
|
||||||
{
|
|
||||||
struct iso_tree_node *node;
|
|
||||||
char *ptr, *brk_info, *component;
|
|
||||||
|
|
||||||
/* get the first child at the root of the volume
|
|
||||||
* that is "/" */
|
|
||||||
node=iso_volume_get_root(volume);
|
|
||||||
if (!strcmp (path, "/"))
|
|
||||||
return node;
|
|
||||||
|
|
||||||
if (!node->nchildren)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* the name of the nodes is in wide characters so first convert path
|
|
||||||
* into wide characters. */
|
|
||||||
ptr = strdup(path);
|
|
||||||
|
|
||||||
/* get the first component of the path */
|
|
||||||
component=strtok_r(ptr, "/", &brk_info);
|
|
||||||
while (component) {
|
|
||||||
size_t max;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
/* search among all the children of this directory if this path component exists */
|
|
||||||
max=node->nchildren;
|
|
||||||
for (i=0; i < max; i++) {
|
|
||||||
if (!strcmp(component, node->children[i]->name)) {
|
|
||||||
node=node->children[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see if a node could be found */
|
|
||||||
if (i==max) {
|
|
||||||
node=NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
component=strtok_r(NULL, "/", &brk_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(ptr);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node *
|
|
||||||
iso_tree_volume_add_path(struct iso_volume *volume,
|
|
||||||
const char *disc_path,
|
|
||||||
const char *path)
|
|
||||||
{
|
|
||||||
char *tmp;
|
|
||||||
struct iso_tree_node *node;
|
|
||||||
struct iso_tree_node *parent_node;
|
|
||||||
|
|
||||||
tmp=strdup(disc_path);
|
|
||||||
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
|
|
||||||
free(tmp);
|
|
||||||
|
|
||||||
if (!parent_node)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
node = iso_tree_radd_dir(parent_node, path);
|
|
||||||
if (!node)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
tmp=strdup(disc_path);
|
|
||||||
iso_tree_node_set_name(node, basename(tmp));
|
|
||||||
free(tmp);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct iso_tree_node *
|
|
||||||
iso_tree_volume_add_new_dir(struct iso_volume *volume,
|
|
||||||
const char *disc_path)
|
|
||||||
{
|
|
||||||
char *tmp;
|
|
||||||
struct iso_tree_node *node;
|
|
||||||
struct iso_tree_node *parent_node;
|
|
||||||
|
|
||||||
tmp=strdup(disc_path);
|
|
||||||
parent_node = iso_tree_volume_path_to_node(volume, dirname(tmp));
|
|
||||||
free(tmp);
|
|
||||||
|
|
||||||
if (!parent_node)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
tmp=strdup(disc_path);
|
|
||||||
node = iso_tree_add_new_dir(parent_node, basename(tmp));
|
|
||||||
free(tmp);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
|
||||||
/* vim: set noet sts=8 ts=8 sw=8 : */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extra declarations for use with the iso_volume structure.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef LIBISO_VOLUME_H
|
|
||||||
#define LIBISO_VOLUME_H
|
|
||||||
|
|
||||||
#include "libisofs.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data volume.
|
|
||||||
*/
|
|
||||||
struct iso_volume
|
|
||||||
{
|
|
||||||
int refcount; /**< Number of used references to this
|
|
||||||
volume. */
|
|
||||||
|
|
||||||
struct iso_tree_node *root; /**< Root of the directory tree for the
|
|
||||||
volume. */
|
|
||||||
|
|
||||||
char *volume_id; /**< Volume identifier. */
|
|
||||||
char *publisher_id; /**< Volume publisher. */
|
|
||||||
char *data_preparer_id; /**< Volume data preparer. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of data volumes.
|
|
||||||
*/
|
|
||||||
struct iso_volset
|
|
||||||
{
|
|
||||||
int refcount;
|
|
||||||
|
|
||||||
struct iso_volume **volume; /**< The volumes belonging to this
|
|
||||||
volume set. */
|
|
||||||
int volset_size; /**< The number of volumes in this
|
|
||||||
volume set. */
|
|
||||||
|
|
||||||
char *volset_id; /**< The id of this volume set, encoded
|
|
||||||
in the current locale. */
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* __ISO_VOLUME */
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user