summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Shadura <andrewsh@debian.org>2016-03-13 09:42:35 +0100
committerAndrew Shadura <andrewsh@debian.org>2016-03-13 09:42:35 +0100
commit9178a1bcd66b04269831fac717ae383a22e2dd63 (patch)
tree018413826c589b9be6d256c40ce10e5e111deab1
Imported Upstream version 0.8.1
-rw-r--r--LICENCE.clearified.artistic136
-rw-r--r--Makefile.in87
-rw-r--r--Session.vim301
-rw-r--r--cd_disect.c2030
-rw-r--r--cd_sessions.c80
-rwxr-xr-xconfig.guess1463
-rwxr-xr-xconfig.sub1555
-rwxr-xr-xconfigure5162
-rw-r--r--configure.ac183
-rw-r--r--defs.h101
-rw-r--r--dirhash.h95
-rw-r--r--ecma167-udf.h844
-rw-r--r--hash.h103
-rwxr-xr-xinstall-sh251
-rw-r--r--mmc_format.c1092
-rw-r--r--newfs_udf.c707
-rw-r--r--osta.c495
-rw-r--r--osta.h45
-rw-r--r--queue.h604
-rw-r--r--udf.c6476
-rw-r--r--udf.h628
-rw-r--r--udf_allocentries.c347
-rw-r--r--udf_bmap.c409
-rw-r--r--udf_bswap.h161
-rw-r--r--udf_discop.c1422
-rw-r--r--udf_discop.h148
-rw-r--r--udf_readwrite.c842
-rw-r--r--udf_unix.c636
-rw-r--r--udf_unix.h165
-rw-r--r--udf_verbose.c1513
-rw-r--r--udfclient.c1781
-rw-r--r--udfdump.c265
-rw-r--r--uio.c87
-rw-r--r--uio.h72
-rw-r--r--uscsi_sense.c835
-rw-r--r--uscsi_subr.c594
-rw-r--r--uscsi_subr.c.async608
-rw-r--r--uscsilib.h116
-rw-r--r--uscsilib_machdep.h91
-rw-r--r--vfs_dirhash.c494
40 files changed, 33024 insertions, 0 deletions
diff --git a/LICENCE.clearified.artistic b/LICENCE.clearified.artistic
new file mode 100644
index 0000000..3e08919
--- /dev/null
+++ b/LICENCE.clearified.artistic
@@ -0,0 +1,136 @@
+ The Clarified Artistic License
+
+
+Preamble:
+---------
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users
+of the package the right to use and distribute the Package in a more-or-less
+customary fashion, plus the right to make reasonable modifications.
+
+
+Definitions:
+------------
+
+"Package" refers to the collection of files distributed by the Copyright
+Holder, and derivatives of that collection of files created through textual
+modification.
+
+"Standard Version" refers to such a Package if it has not been modified, or
+has been modified in accordance with the wishes of the Copyright Holder as
+specified below.
+
+"Copyright Holder" is whoever is named in the copyright or copyrights for the
+package.
+
+"You" is you, if you're thinking about copying or distributing this Package.
+
+"Distribution fee" is a fee you charge for providing a copy of this Package to
+another party.
+
+"Freely Available" means that no fee is charged for the right to use the item,
+though there may be fees involved in handling the item. It also means that
+recipients of the item may redistribute it under the same conditions they
+received it.
+
+
+Licence:
+--------
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived
+from the Public Domain, or those made Freely Available, or from the Copyright
+Holder. A Package modified in such a way shall still be considered the
+Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and when
+you changed that file, and provided that you do at least ONE of the following:
+
+ 1. place your modifications in the Public Domain or otherwise make
+ them Freely Available, such as by posting said modifications to Usenet
+ or an equivalent medium, or placing the modifications on a major
+ network archive site allowing unrestricted access to them, or by
+ allowing the Copyright Holder to include your modifications in the
+ Standard Version of the Package.
+
+ 2. use the modified Package only within your corporation or organization.
+
+ 3. rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide a
+ separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ 4. make other distribution arrangements with the Copyright Holder.
+
+ 5. permit and encourge anyone who receives a copy of the modified
+ Package permission to make your modifications Freely Available in some
+ specific way.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ 1. distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ 2. accompany the distribution with the machine-readable source of the
+ Package with your modifications.
+
+ 3. give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+
+ 4. make other distribution arrangements with the Copyright Holder.
+
+ 5. offer the machine-readable source of the Package, with your
+ modifications, by mail order.
+
+5. You may charge a distribution fee for any distribution of this Package. If
+you offer support for this Package, you may charge any fee you choose for that
+support. You may not charge a license fee for the right to use this Package
+itself. You may distribute this Package in aggregate with other (possibly
+commercial and possibly nonfree) programs as part of a larger (possibly
+commercial and possibly nonfree) software distribution, and charge license
+fees for other parts of that software distribution, provided that you do not
+advertise this Package as a product of your own. If the Package includes an
+interpreter, You may embed this Package's interpreter within an executable of
+yours (by linking); this shall be construed as a mere form of aggregation,
+provided that the complete Standard Version of the interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as output
+from the programs of this Package do not automatically fall under the
+copyright of this Package, but belong to whoever generated them, and may be
+sold commercially, and may be aggregated with this Package. If such scripts or
+library files are aggregated with this Package via the so-called "undump" or
+"unexec" methods of producing a binary executable image, then distribution of
+such an image shall neither be construed as a distribution of this Package nor
+shall it fall under the restrictions of Paragraphs 3 and 4, provided that you
+do not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other languages)
+supplied by you and linked into this Package in order to emulate subroutines
+and variables of the language defined by this Package shall not be considered
+part of this Package, but are the equivalent of input as in Paragraph 6,
+provided these subroutines do not change the language in any way that would
+cause it to fail the regression tests for the language.
+
+8. Aggregation of the Standard Version of the Package with a commercial
+distribution is always permitted provided that the use of this Package is
+embedded; that is, when no overt attempt is made to make this Package's
+interfaces visible to the end user of the commercial distribution. Such use
+shall not be construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..f29bf74
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,87 @@
+#
+# UDFclient toolkit
+#
+# Copyright 2003,2004,2005 Reinoud P.Zandijk
+#
+#
+# File $Id: Makefile.in,v 1.20 2009/07/24 16:51:49 reinoud Exp $ $Name: $
+#
+
+srcdir= @srcdir@
+
+prefix= @prefix@
+exec_prefix= @exec_prefix@
+sbindir= @sbindir@
+mandir= @mandir@
+bindir= @bindir@
+datarootdir= @datarootdir@
+
+CC= @CC@
+LD= $(CC)
+CPPFLAGS= @CPPFLAGS@ @DEFS@ -I$(srcdir)
+CFLAGS= @CFLAGS@
+LDFLAGS= @LDFLAGS@
+
+INSTALL= @INSTALL@
+
+# NetBSD
+#CFLAGS+= -Wall \
+# -Wuninitialized \
+# -fno-strict-aliasing \
+# -Wextra -Werror
+# -Werror ${COPTS} \
+# -DNDEBUG
+
+# Anonymising this client (for comformity)
+#CFLAGS+= -D__ANONYMOUSUDF__
+
+#CFLAGS+= -fPIC -DPIC
+POSTOBJ+= @SCSI_LIB@ @THREADLIB@ @TIMELIB@
+
+
+# For use with the cpuflags package
+#CPROCFLAGS != cpuflags
+CFLAGS+= ${CPROCFLAGS}
+
+
+# Main programs
+APPS= udfdump udfclient newfs_udf cd_sessions
+SCSI_APPS= cd_disect mmc_format
+LIB= osta.o udf.o udf_bmap.o udf_allocentries.o udf_discop.o uio.o
+LIB+= udf_verbose.o udf_readwrite.o udf_unix.o vfs_dirhash.o
+USCSILIB= uscsi_sense.o uscsi_subr.o
+
+CFLAGS+= -DNEEDS_ISPRINT $(CPPFLAGS)
+
+all: @BUILD_APPS@
+
+install: @BUILD_APPS@
+ for app in $(APPS) $(SCSI_APPS); do \
+ echo "$(INSTALL) $$app $(bindir)/$$app"; \
+ $(INSTALL) $$app $(bindir)/$$app; \
+ done
+
+depend:
+ mkdep $(CFLAGS) *.c
+
+clean:
+ rm -f *.o *.a *.core core.* core .depend *~ *.bak $(APPS) $(SCSI_APPS)
+ rm -fr autom*.cache config.log config.status configure.lineno
+
+cleandir: clean
+ rm -f Makefile
+
+libuscsi.a: $(USCSILIB)
+ ar -rsc libuscsi.a $(USCSILIB)
+
+libudf.a: $(LIB)
+ ar -rsc libudf.a $(LIB)
+
+$(SCSI_APPS): $(.TARGET).o libuscsi.a
+ $(LD) -o $(.TARGET) $(LDFLAGS) $(.TARGET).o libuscsi.a $(POSTOBJ)
+
+$(APPS): $(.TARGET).o libuscsi.a libudf.a
+ $(LD) -o $(.TARGET) $(LDFLAGS) $(.TARGET).o libudf.a libuscsi.a $(POSTOBJ)
+
+
+# DO NOT DELETE
diff --git a/Session.vim b/Session.vim
new file mode 100644
index 0000000..e8fbc66
--- /dev/null
+++ b/Session.vim
@@ -0,0 +1,301 @@
+let SessionLoad = 1
+if &cp | set nocp | endif
+let s:cpo_save=&cpo
+set cpo&vim
+imap <S-Right> <Down><Home>
+imap <S-Left> <Up><End>
+map  :bn<NL>
+map  :bp<NL>
+map Q gq
+nmap gx <Plug>NetrwBrowseX
+nnoremap <silent> <Plug>NetrwBrowseX :call netrw#NetrwBrowseX(expand("<cWORD>"),0)
+imap  gq{gq}
+imap  c \cite{p}
+let &cpo=s:cpo_save
+unlet s:cpo_save
+set autoindent
+set background=dark
+set backspace=2
+set backup
+set cindent
+set comments=sr:/*,mb:*,el:*/,://
+set formatoptions=croql
+set guifont=Monospace\ 8
+set hidden
+set highlight=8:SpecialKey,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:IncSearch,m:MoreMsg,M:ModeMsg,n:LineNr,r:Question,s:StatusLine,S:StatusLineNC,t:Title,v:Visual,w:WarningMsg
+set hlsearch
+set mouse=a
+set ruler
+set termencoding=utf-8
+set textwidth=78
+set visualbell
+set window=79
+let s:so_save = &so | let s:siso_save = &siso | set so=0 siso=0
+let v:this_session=expand("<sfile>:p")
+silent only
+cd ~/UDF/src
+if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''
+ let s:wipebuf = bufnr('%')
+endif
+set shortmess=aoO
+badd +41 cd_disect.c
+badd +1 cd_sessions.c
+badd +1 config.h
+badd +1 defs.h
+badd +1 dirhash.h
+badd +136 ecma167-udf.h
+badd +1 mmc_format.c
+badd +31 newfs_udf.c
+badd +1 osta.c
+badd +1 osta.h
+badd +1 queue.h
+badd +5182 udf.c
+badd +527 udf.h
+badd +1 udf_allocentries.c
+badd +1 udf_bmap.c
+badd +1 udf_bswap.h
+badd +1 udf_discop.c
+badd +1 udf_discop.h
+badd +1 udf_readwrite.c
+badd +1 udf_unix.c
+badd +1 udf_unix.h
+badd +1370 udf_verbose.c
+badd +714 udfclient.c
+badd +206 udfdump.c
+badd +1 uio.c
+badd +1 uio.h
+badd +1 uscsi_sense.c
+badd +1 uscsi_subr.c
+badd +1 uscsilib.h
+badd +1 uscsilib_machdep.h
+badd +423 vfs_dirhash.c
+badd +554 /usr/src/sys/fs/udf/udf_vnops.c
+badd +4684 /usr/src/sys/fs/udf/udf_subr.c
+args cd_disect.c cd_sessions.c config.h defs.h dirhash.h ecma167-udf.h mmc_format.c newfs_udf.c osta.c osta.h queue.h udf.c udf.h udf_allocentries.c udf_bmap.c udf_bswap.h udf_discop.c udf_discop.h udf_readwrite.c udf_unix.c udf_unix.h udf_verbose.c udfclient.c udfdump.c uio.c uio.h uscsi_sense.c uscsi_subr.c uscsilib.h uscsilib_machdep.h vfs_dirhash.c
+edit vfs_dirhash.c
+set splitbelow splitright
+wincmd _ | wincmd |
+split
+1wincmd k
+wincmd w
+set nosplitbelow
+set nosplitright
+wincmd t
+set winheight=1 winwidth=1
+exe '1resize ' . ((&lines * 26 + 40) / 80)
+exe '2resize ' . ((&lines * 51 + 40) / 80)
+argglobal
+edit vfs_dirhash.c
+setlocal autoindent
+setlocal balloonexpr=
+setlocal nobinary
+setlocal bufhidden=
+setlocal buflisted
+setlocal buftype=
+setlocal cindent
+setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e
+setlocal cinoptions=
+setlocal cinwords=if,else,while,do,for,switch
+setlocal comments=sr:/*,mb:*,el:*/,://
+setlocal commentstring=/*%s*/
+setlocal complete=.,w,b,u,t,i
+setlocal completefunc=
+setlocal nocopyindent
+setlocal nocursorcolumn
+setlocal nocursorline
+setlocal define=
+setlocal dictionary=
+setlocal nodiff
+setlocal equalprg=
+setlocal errorformat=
+setlocal noexpandtab
+if &filetype != 'c'
+setlocal filetype=c
+endif
+setlocal foldcolumn=0
+setlocal foldenable
+setlocal foldexpr=0
+setlocal foldignore=#
+setlocal foldlevel=0
+setlocal foldmarker={{{,}}}
+setlocal foldmethod=manual
+setlocal foldminlines=1
+setlocal foldnestmax=20
+setlocal foldtext=foldtext()
+setlocal formatexpr=
+setlocal formatoptions=croql
+setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s*
+setlocal grepprg=
+setlocal iminsert=0
+setlocal imsearch=0
+setlocal include=
+setlocal includeexpr=
+setlocal indentexpr=
+setlocal indentkeys=0{,0},:,0#,!^F,o,O,e
+setlocal noinfercase
+setlocal iskeyword=@,48-57,_,192-255
+setlocal keywordprg=
+setlocal nolinebreak
+setlocal nolisp
+setlocal nolist
+setlocal makeprg=
+setlocal matchpairs=(:),{:},[:]
+setlocal modeline
+setlocal modifiable
+setlocal nrformats=octal,hex
+setlocal nonumber
+setlocal numberwidth=4
+setlocal omnifunc=
+setlocal path=
+setlocal nopreserveindent
+setlocal nopreviewwindow
+setlocal quoteescape=\\
+setlocal noreadonly
+setlocal noscrollbind
+setlocal shiftwidth=8
+setlocal noshortname
+setlocal nosmartindent
+setlocal softtabstop=0
+setlocal nospell
+setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+
+setlocal spellfile=
+setlocal spelllang=en
+setlocal statusline=
+setlocal suffixesadd=
+setlocal swapfile
+setlocal synmaxcol=3000
+if &syntax != 'c'
+setlocal syntax=c
+endif
+setlocal tabstop=8
+setlocal tags=
+setlocal textwidth=78
+setlocal thesaurus=
+setlocal nowinfixheight
+setlocal nowinfixwidth
+setlocal wrap
+setlocal wrapmargin=0
+silent! normal! zE
+let s:l = 341 - ((19 * winheight(0) + 13) / 26)
+if s:l < 1 | let s:l = 1 | endif
+exe s:l
+normal! zt
+341
+normal! 0
+wincmd w
+argglobal
+edit udf.c
+setlocal autoindent
+setlocal balloonexpr=
+setlocal nobinary
+setlocal bufhidden=
+setlocal buflisted
+setlocal buftype=
+setlocal cindent
+setlocal cinkeys=0{,0},0),:,0#,!^F,o,O,e
+setlocal cinoptions=
+setlocal cinwords=if,else,while,do,for,switch
+setlocal comments=sr:/*,mb:*,el:*/,://
+setlocal commentstring=/*%s*/
+setlocal complete=.,w,b,u,t,i
+setlocal completefunc=
+setlocal nocopyindent
+setlocal nocursorcolumn
+setlocal nocursorline
+setlocal define=
+setlocal dictionary=
+setlocal nodiff
+setlocal equalprg=
+setlocal errorformat=
+setlocal noexpandtab
+if &filetype != 'c'
+setlocal filetype=c
+endif
+setlocal foldcolumn=0
+setlocal foldenable
+setlocal foldexpr=0
+setlocal foldignore=#
+setlocal foldlevel=0
+setlocal foldmarker={{{,}}}
+setlocal foldmethod=manual
+setlocal foldminlines=1
+setlocal foldnestmax=20
+setlocal foldtext=foldtext()
+setlocal formatexpr=
+setlocal formatoptions=croql
+setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s*
+setlocal grepprg=
+setlocal iminsert=0
+setlocal imsearch=0
+setlocal include=
+setlocal includeexpr=
+setlocal indentexpr=
+setlocal indentkeys=0{,0},:,0#,!^F,o,O,e
+setlocal noinfercase
+setlocal iskeyword=@,48-57,_,192-255
+setlocal keywordprg=
+setlocal nolinebreak
+setlocal nolisp
+setlocal nolist
+setlocal makeprg=
+setlocal matchpairs=(:),{:},[:]
+setlocal modeline
+setlocal modifiable
+setlocal nrformats=octal,hex
+setlocal nonumber
+setlocal numberwidth=4
+setlocal omnifunc=
+setlocal path=
+setlocal nopreserveindent
+setlocal nopreviewwindow
+setlocal quoteescape=\\
+setlocal noreadonly
+setlocal noscrollbind
+setlocal shiftwidth=8
+setlocal noshortname
+setlocal nosmartindent
+setlocal softtabstop=0
+setlocal nospell
+setlocal spellcapcheck=[.?!]\\_[\\])'\"\ \ ]\\+
+setlocal spellfile=
+setlocal spelllang=en
+setlocal statusline=
+setlocal suffixesadd=
+setlocal swapfile
+setlocal synmaxcol=3000
+if &syntax != 'c'
+setlocal syntax=c
+endif
+setlocal tabstop=8
+setlocal tags=
+setlocal textwidth=78
+setlocal thesaurus=
+setlocal nowinfixheight
+setlocal nowinfixwidth
+setlocal wrap
+setlocal wrapmargin=0
+silent! normal! zE
+let s:l = 5895 - ((22 * winheight(0) + 25) / 51)
+if s:l < 1 | let s:l = 1 | endif
+exe s:l
+normal! zt
+5895
+normal! 0
+wincmd w
+2wincmd w
+exe '1resize ' . ((&lines * 26 + 40) / 80)
+exe '2resize ' . ((&lines * 51 + 40) / 80)
+tabnext 1
+if exists('s:wipebuf')
+ silent exe 'bwipe ' . s:wipebuf
+endif
+unlet! s:wipebuf
+set winheight=1 winwidth=20 shortmess=filnxtToO
+let s:sx = expand("<sfile>:p:r")."x.vim"
+if file_readable(s:sx)
+ exe "source " . s:sx
+endif
+let &so = s:so_save | let &siso = s:siso_save
+doautoall SessionLoadPost
+unlet SessionLoad
+" vim: set ft=vim :
diff --git a/cd_disect.c b/cd_disect.c
new file mode 100644
index 0000000..b79002f
--- /dev/null
+++ b/cd_disect.c
@@ -0,0 +1,2030 @@
+/* $NetBSD$ */
+
+/*
+ * File "cd_disect.c" is part of the UDFclient toolkit.
+ * File $Id: cd_disect.c,v 1.79 2015/08/05 18:26:28 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "uscsilib.h"
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/*
+ * from inquiry preriph. device type; used to saveguard MMC specific
+ * operations. See spc2.
+ */
+
+#define DEVICE_TYPE_MMC 0x05
+
+
+/* globals */
+struct uscsi_dev dev;
+
+
+/* helper functions */
+static int read_cd_hex2(int val) {
+ int nl, nh;
+
+ nl = val & 15;
+ nh = val >> 4;
+ if (nl >= 'A') nl -= 'A' + 10;
+ if (nh >= 'A') nh -= 'A' + 10;
+
+ return (nh*16) + nl;
+}
+
+
+static int read_cd_bcd(int val) {
+ int nl, nh;
+
+ nl = (val & 15) - '0';
+ nh = (val >> 4) - '0';
+ if ((nl < 0 || nl > 9) || (nh < 0 || nh > 9)) return val;
+
+ return nh*10 + nl;
+}
+
+
+static int32_t cd_msf2lba(int h, int m, int s, int f) {
+ return 270000*h + 4500*m + 75*s + f - 150;
+}
+
+
+
+/* start of discect functions */
+static char *print_disc_type(int type) {
+ switch (type) {
+ case 0x00 : return "CD-DA or CD-ROM Disc or non CD";
+ case 0x10 : return "CD-I Disc";
+ case 0x20 : return "CD-ROM XA Disc";
+ case 0xFF : return "Undefined";
+ }
+ return "Reserved";
+}
+
+
+static char *print_disc_state(int state) {
+ switch (state) {
+ case 0 : return "empty disc";
+ case 1 : return "incomplete (appendable)";
+ case 2 : return "full (not appendable)";
+ case 3 : return "random writable";
+ }
+ return "unknown disc state";
+}
+
+
+static char *print_session_state(int state) {
+ switch (state) {
+ case 0 : return "empty";
+ case 1 : return "incomplete";
+ case 2 : return "reserved/damaged";
+ case 3 : return "complete/closed disc";
+ }
+ return "unknown session_state";
+}
+
+
+static char *print_mmc_profile(int profile) {
+ static char scrap[100];
+
+ switch (profile) {
+ case 0x00 : return "Unknown[0] profile";
+ case 0x01 : return "Non removeble disc";
+ case 0x02 : return "Removable disc";
+ case 0x03 : return "Magneto Optical with sector erase";
+ case 0x04 : return "Magneto Optical write once";
+ case 0x05 : return "Advance Storage Magneto Optical";
+ case 0x08 : return "CD-ROM";
+ case 0x09 : return "CD-R recordable";
+ case 0x0a : return "CD-RW rewritable";
+ case 0x10 : return "DVD-ROM";
+ case 0x11 : return "DVD-R sequential";
+ case 0x12 : return "DVD-RAM rewritable";
+ case 0x13 : return "DVD-RW restricted overwrite";
+ case 0x14 : return "DVD-RW sequential";
+ case 0x15 : return "DVD-R dual layer sequential";
+ case 0x16 : return "DVD-R dual layer jump";
+ case 0x17 : return "DVD-RW dual layer";
+ case 0x18 : return "DVD-Download disc";
+ case 0x1a : return "DVD+RW rewritable";
+ case 0x1b : return "DVD+R recordable";
+ case 0x20 : return "DDCD readonly (retracted)";
+ case 0x21 : return "DDCD-R recordable (retracted)";
+ case 0x22 : return "DDCD-RW rewritable (retracted)";
+ case 0x2a : return "DVD+RW double layer";
+ case 0x2b : return "DVD+R double layer";
+ case 0x40 : return "BD-ROM";
+ case 0x41 : return "BD-R Sequential Recording (SRM)";
+ case 0x42 : return "BD-R Random Recording (RRM)";
+ case 0x43 : return "BD-RE rewritable";
+ case 0x50 : return "HD DVD-ROM (retracted)";
+ case 0x51 : return "HD DVD-R (retracted)";
+ case 0x52 : return "HD DVD-RAM (retracted)";
+ case 0x53 : return "HD DVD-RW (retracted)";
+ case 0x58 : return "HD DVD-R dual layer (retracted)";
+ case 0x5a : return "HD DVD-RW dual layer (retracted)";
+ }
+ sprintf(scrap, "Reserved profile 0x%02x", profile);
+ return scrap;
+}
+
+
+static char *print_write_type(int type) {
+ switch (type) {
+ case 0x00 : return "Packet/Incremental";
+ case 0x01 : return "Track-at-once";
+ case 0x02 : return "Session-at-one";
+ case 0x03 : return "Raw";
+ }
+ return "unknown write type";
+}
+
+
+static char *print_data_block_type(int type) {
+ static char scrap[100];
+
+ switch (type) {
+ case 0 : return "raw data 2352 bytes";
+ case 1 : return "raw data 2368 bytes with P and Q channel";
+ case 2 : return "raw data 2352 bytes (+96) P-W subchannel appended";
+ case 3 : return "raw data 2352 bytes (+96) raw P-W subchannel appended";
+ case 8 : return "ISO mode 1 with 2048 bytes data";
+ case 9 : return "ISO mode 2 with 2336 bytes data, formless";
+ case 10 : return "ISO mode 2 with 2048 bytes data (CDROM-XA, form 1), subheader from write parameters";
+ case 11 : return "ISO mode 2 with 2048 bytes data (CDROM-XA, form 1), 8 bytes for subheader first";
+ case 12 : return "ISO mode 2 with 2324 bytes data (CDROM-XA, form 2), subheader from write parameters";
+ case 13 : return "ISO mode 2 with 2332 bytes data (CDROM-XA, form 1 or 2 or mixed), 8 bytes for subheader first";
+ }
+ sprintf(scrap, "Unknown/reserved data block type 0x%02x", type);
+ return scrap;
+}
+
+
+static char *print_Q_control(int cntrl) {
+ static char scrap[100];
+
+ strcpy(scrap, "");
+ if ((cntrl & 12) == 4) {
+ strcat(scrap, "data track ");
+ if (cntrl & 1) strcat(scrap, "; incremental "); else strcat(scrap, "; uninterrupted");
+ } else {
+ strcat(scrap, "audio track");
+ if (cntrl & 1) strcat(scrap, "; pre-emphasis of 50/15 µs"); else strcat(scrap, "; no pre-emphasis");
+ }
+ if (cntrl & 2) strcat(scrap, "; copy prohibited");
+
+ return scrap;
+}
+
+
+static char *print_session_format(int format) {
+ static char scrap[100];
+
+ switch (format) {
+ case 0x00 : return "CD-DA, CD-ROM or other data discs";
+ case 0x10 : return "CD-I disc";
+ case 0x20 : return "CD-ROM XA disc or DDCD disc";
+ }
+ sprintf(scrap, "Unknown/reserved session format type 0x%02x", format);
+ return scrap;
+}
+
+
+/* why disc_type is not equal to session_format is not clear yet; its reported in the TOC/PMA/ATI format 010b */
+static char *print_TOC_disc_type(int type) {
+ static char scrap[100];
+
+ switch (type) {
+ case 0x00 : return "CD-DA or CD Data disc with first track in Mode 1";
+ case 0x10 : return "CD-I disc";
+ case 0x20 : return "CD data XA disc with first track in Mode 2";
+ }
+ sprintf(scrap, "Unknown/reserved TOC disc type type 0x%02x", type);
+ return scrap;
+}
+
+
+static char *print_inactivity_time(int time) {
+ static char scrap[100];
+
+ switch (time) {
+ case 0x0 : return "Vendor specific";
+ case 0x1 : return "125 ms";
+ case 0x2 : return "250 ms";
+ case 0x3 : return "500 ms";
+ case 0x4 : return "1 sec";
+ case 0x5 : return "2 sec";
+ case 0x6 : return "4 sec";
+ case 0x7 : return "8 sec";
+ case 0x8 : return "16 sec";
+ case 0x9 : return "32 sec";
+ case 0xa : return "1 min";
+ case 0xb : return "2 min";
+ case 0xc : return "4 min";
+ case 0xd : return "8 min";
+ case 0xe : return "16 min";
+ case 0xf : return "32 min";
+ }
+ sprintf(scrap, "Unknown/reserved inactivity timeout 0x%02x", time);
+ return scrap;
+}
+
+
+static char *printdevice_type(int type) {
+ static char scrap[100];
+
+ switch (type) {
+ case 0x00 : return "Direct-access device (e.g., magnetic disk)";
+ case 0x01 : return "Sequential-access device (e.g., magnetic tape)";
+ case 0x02 : return "Printer device";
+ case 0x03 : return "Processor device";
+ case 0x04 : return "Write-once device (e.g., some optical disks)";
+ case 0x05 : return "CD-ROM device";
+ case 0x06 : return "Scanner";
+ case 0x07 : return "Optical memory device (e.g., some optical disks)";
+ case 0x08 : return "Medium changer device (e.g., jukeboxes)";
+ case 0x09 : return "Communications device";
+ case 0x0a : /* fall trough */
+ case 0x0b : return "Defined by ASC IT8 (Graphic arts pre-press devices)";
+ case 0x0c : return "Storage array controller device (e.g., RAID)";
+ case 0x0d : return "Enclosure services device";
+ case 0x0e : return "Simplified direct-access device (e.g., magnetic disk)";
+ case 0x0f : return "Optical card reader/writer device";
+ case 0x10 : return "Reserved/used for Bridging Expanders";
+ case 0x11 : return "Object-based Storage Device";
+ }
+ sprintf(scrap, "Unknown/reserved device type 0x%02x", type);
+ return scrap;
+}
+
+
+static char *printdevice_qualifier(int device_qualifier) {
+ static char scrap[100];
+
+ switch (device_qualifier) {
+ case 0x00 : return "Device server is capable and device is connected";
+ case 0x01 : return "Device server is capable but device is not connected";
+ case 0x02 : return "Reserved";
+ case 0x03 : return "Device server is not capable of supporting this device";
+ }
+ sprintf(scrap, "Unknown/reserved device qualifier 0x%02x", device_qualifier);
+ return scrap;
+}
+
+
+static char *printstandards_version(int version) {
+ static char scrap[100];
+
+ switch (version) {
+ case 0x00 : return "Does not claim conformance to any standard";
+ case 0x01 : return "SCSI (obsolete)";
+ case 0x03 : return "The device complies to ANSI INCITS 301-1997 (SPC)";
+ case 0x04 : return "The device complies to ANSI INCITS 351-2001 (SPC-2)";
+ case 0x05 : return "The device complies to ANSI INCITS T10/1416-D (SPC-3)";
+ }
+ sprintf(scrap, "Unknown/Obsolete/reserved (0x%02x)", version);
+ return scrap;
+}
+
+
+static char *print_normal_string(uint8_t *buf, int len) {
+ static char scrap[100];
+ char *pos;
+ int i;
+
+ memset(scrap, 0, 100);
+ pos = scrap;
+ for (i = 0; i < len; i++) {
+ if (isprint(buf[i]))
+ *pos++ = buf[i];
+ }
+ *pos = (char) 0;
+ return scrap;
+}
+
+
+void dump_drive_identify(int *device_type) {
+ scsicmd cmd;
+ uint8_t buf[100];
+ int device_qual, rmb, version, additional_len, total_len;
+ int i, error;
+
+ /* go for SCB for a start */
+ *device_type = 0;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x12; /* INQUIRY */
+ cmd[1] = 0; /* basic inquiry */
+ cmd[2] = 0; /* no page or operation code */
+ cmd[3] = 0; /* reserved/MSB result */
+ cmd[4] = 96; /* all but vendor specific */
+ cmd[5] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 6, buf, 96, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "Device Inquiry returned error : %s\n", strerror(error));
+ return;
+ }
+
+ *device_type = buf[0] & 0x1f;
+ device_qual = buf[0] >> 16;
+ rmb = buf[1] & 0x80;
+ version = buf[2];
+ additional_len = buf[4];
+ total_len = additional_len + 4;
+
+ printf("\n");
+ printf("\tDevice qualfier\t\t: %s\n", printdevice_qualifier(device_qual));
+ printf("\tDevice type\t\t: %s\n", printdevice_type(*device_type));
+ printf("\tMedia type\t\t: %s\n", rmb? "Removable": "Fixed");
+ printf("\tConforming to standard\t: %s\n", printstandards_version(version));
+ printf("\tVendor identification\t: %s\n", print_normal_string(buf + 8, 8));
+ printf("\tProduct identification\t: %s\n", print_normal_string(buf + 16, 16));
+ printf("\tProduct revision level\t: %s\n", print_normal_string(buf + 32, 4));
+ if (total_len < 36)
+ goto out;
+ printf("\tVendor specific\t\t: %s\n", print_normal_string(buf + 36, 19));
+ if (total_len < 58)
+ goto out;
+
+ printf("\tComplies to:\n");
+ for (i = 58; i <= 72; i+=2) {
+ if (i >= total_len)
+ break;
+ version = buf[i+1] | (buf[i] << 8);
+ printf("\t\t0x%04x\n", version);
+ }
+out:
+ printf("\t<Rest not dumped yet>\n");
+ printf("\n");
+}
+
+
+void dump_recorded_capacity(void) {
+ scsicmd cmd;
+ uint8_t buf[36];
+ uint32_t lba, blk_len;
+ int error;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, 8, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "Read recorded capacity SCSI call returned : %s\n", strerror(error));
+ return;
+ }
+
+ lba = buf[3] | (buf[2]<<8) | (buf[1]<<16) | (buf[0]<<24);
+ blk_len = buf[7] | (buf[6]<<8) | (buf[5]<<16) | (buf[4]<<24);
+ printf("\nCD recorded capacity is LBA %d (%d blocks), blk_len %d\n",
+ lba, lba/4, blk_len);
+}
+
+
+void dump_feature(uint8_t *fpos) {
+ uint32_t feature, cnt, profile;
+ uint32_t feature_len, feature_ver, feature_cur, feature_pers;
+ uint32_t interface, blocking, log_blk_size, last_log_blk_addr;
+ uint32_t datablock_types, num_link_sizes, cuesheet_len;
+ uint32_t num_vol_levels, dcb_entry;
+ uint32_t century, year, day, month, hour, minute, second;
+ uint8_t *pos;
+
+ feature = fpos[1] | (fpos[0] << 8);
+ feature_ver = (fpos[2] >> 2) & 15;
+ feature_cur = (fpos[2] & 1);
+ feature_pers= (fpos[2] & 2);
+ feature_len = fpos[3];
+ printf("\t\tFeature 0x%04x (%2d bytes) version %2d; persistent %s; currently active %s\n",
+ feature, feature_len, feature_ver, feature_pers?"yes":" no", feature_cur?"yes":" no");
+
+ pos = &fpos[4];
+ switch (feature) {
+ case 0x0000:
+ printf("\t\tProfile list; supporting profiles\n");
+ for (cnt=0; cnt < feature_len; cnt += 4) {
+ profile = pos[1] | (pos[0] << 8);
+ printf("\t\t\t%s %s\n", pos[2] & 1 ?"ACTIVE ":"inactive", print_mmc_profile(profile));
+ pos += 4;
+ }
+ break;
+ case 0x0001:
+ interface = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
+ printf("\t\tCore features : physical interface standard commands for `");
+ switch (interface) {
+ case 0 : printf("Unspecified"); break;
+ case 1 : printf("SCSI family"); break;
+ case 2 : printf("ATAPI"); break;
+ case 3 : printf("IEEE 1394 - 1995"); break;
+ case 4 : printf("IEEE 1394A"); break;
+ case 5 : printf("Fibre channel"); break;
+ case 6 : printf("IEEE 1394B"); break;
+ case 7 : printf("Serial ATAPI"); break;
+ case 8 : printf("USB (1.1 or 2.0)"); break;
+ case 0xffff : printf("Vendor specific"); break;
+ default : printf("<reserved (0x%04x)>", interface); break;
+ }
+ printf("'\n");
+ break;
+ case 0x0002:
+ printf("\t\tMorphing command set; %ssupport for ASYNC\n", (pos[0] & 1)?"":"no ");
+ break;
+ case 0x0003:
+ printf("\t\tRemovable medium features\n");
+ if (feature_ver > 1) {
+ printf("\t\t\tUnknown flags\n");
+ break;
+ }
+ if (pos[0] & 1) printf("\t\t\tDevice can be locked against removal\n");
+ printf("\t\t\tDevice will go into the %slocked state by default\n", (pos[0] & 4)?"":"un-");
+ printf("\t\t\tDevice %s eject the media with START/STOP with eject bit\n", (pos[0] & 8)?"can":"can't");
+ printf("\t\t\tLoading mechanism : ");
+ switch (pos[0] >> 5) {
+ case 0 : printf("Caddy"); break;
+ case 1 : printf("Tray"); break;
+ case 2 : printf("Pop-up"); break;
+ case 4 : printf("Embedded changer with separate discs"); break;
+ case 5 : printf("Embedded changes using magazines"); break;
+ default : printf("Unknown/Reserved");
+ }
+ printf("\n");
+ break;
+ case 0x0004:
+ printf("\t\tWrite protect features :");
+ if (pos[0] & 2) printf(" SPWP");
+ if (pos[0] & 1) printf(" SSWPP");
+ printf("\n");
+ break;
+ case 0x0010:
+ printf("\t\tRandom readable\n");
+ log_blk_size = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
+ blocking = pos[5] | (pos[4] << 8);
+ printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
+ printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
+ if (pos[6] & 1) printf("\t\t\tHas RW error recovery mode page\n");
+ break;
+ case 0x001d:
+ printf("\t\tMulti-read; The Logical Unit can read all CD media types; based on OSTA MultiRead\n");
+ break;
+ case 0x001e:
+ printf("\t\tAbility to read CD specific structures\n");
+ printf("\t\t\tDevice does %ssupport C2 error pointers\n", (pos[0] & 2)?"":"NOT ");
+ printf("\t\t\tDevice does %ssupport CD-Text\n", (pos[0] & 1)?"":"NOT ");
+ break;
+ case 0x001f:
+ printf("\t\tAbility to read DVD specific structures\n");
+ break;
+ case 0x0020:
+ printf("\t\tRandom writable\n");
+ last_log_blk_addr = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
+ log_blk_size = pos[7] | (pos[6] << 8) | (pos[5] << 16) | (pos[4] << 24);
+ blocking = pos[9] | (pos[8] << 8);
+ printf("\t\t\tLast writable logic block address is %u ", last_log_blk_addr);
+ printf("(approx %u Mb)\n", (uint32_t) ((uint64_t) last_log_blk_addr*log_blk_size/(1024*1024)));
+ printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
+ printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
+ if (pos[10] & 1) printf("\t\t\tHas RW error recovery mode page\n");
+ break;
+ case 0x0021:
+ printf("\t\tIncremental streaming writable\n");
+ datablock_types = pos[1] | (pos[0] << 8);
+ num_link_sizes = pos[3];
+ printf("\t\t\tDevice is %scapable of `Zero loss linking'\n", (pos[2] & 1)?"":"NOT ");
+ printf("\t\t\tDevice supported data types (bitfield) 0x%04x\n", datablock_types);
+ printf("\t\t\tDevice supports %d link size%s :", num_link_sizes, (num_link_sizes != 1)?"s":"");
+ for (cnt=0; cnt < num_link_sizes; cnt++) {
+ printf(" %d", pos[4+cnt]);
+ }
+ printf("\n");
+ break;
+ case 0x0022:
+ printf("\t\tCan erase/support for erasing media (OBSOLETE)\n");
+ break;
+ case 0x0023:
+ printf("\t\tSupport for formatting media\n");
+ break;
+ case 0x0024:
+ printf("\t\tHas defect handling; i.e. apparently defect-free space\n");
+ printf("\t\t\tDevice does %ssupport read `Space Area Information' (DVD)\n", (pos[0] & 128)?"":"NOT ");
+ break;
+ case 0x0025:
+ printf("\t\tSupport for writing any unrecorded logical block on write once media in random order\n");
+ log_blk_size = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
+ blocking = pos[5] | (pos[4] << 8);
+ printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
+ printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
+ if (pos[6] & 1) printf("\t\t\tHas RW error recovery mode page\n");
+ break;
+ case 0x0026:
+ printf("\t\tSupport for restricted overwrite; i.e. on blocking boundaries only\n");
+ break;
+ case 0x0027:
+ printf("\t\tCD-RW CAV Write; The ability to write high speed CD-RW media\n");
+ /* parameters all reserved */
+ break;
+ case 0x0028:
+ printf("\t\tMRW formatted media support\n");
+ printf("\t\t\tDevice can read MRW formatted media\n");
+ printf("\t\t\tDevice %swrite/format media in MRW format\n", (pos[0] & 1)?"can ":"can't");
+ break;
+ case 0x002a:
+ printf("\t\tDVD+RW media reading/writing support\n");
+ if (pos[0] & 1) {
+ printf("\t\t\tDevice can write and background format DVD+RW media\n");
+ if (pos[1] & 1) printf("\t\t\tDevice only supports read compatibility format stop\n");
+ } else {
+ printf("\t\t\tDevice can only recognise and read DVD+RW\n");
+ }
+ break;
+ case 0x002b:
+ printf("\t\tThe ability to read/write DVD+R recorded media\n");
+ if (pos[0] & 1) {
+ printf("\t\t\tDevice can write DVD+R media\n");
+ } else {
+ printf("\t\t\tDevice can only recognise and read DVD+R\n");
+ }
+ break;
+ case 0x002c:
+ printf("\t\tSupport for rigid restricted overwrite only (CD-RW)\n");
+ if (pos[0] & 8) printf("\t\t\tDevice can generate direct status data during formatting\n");
+ if (pos[0] & 4) printf("\t\t\tDevice can read the defect status data recorded on media\n");
+ if (pos[0] & 2) printf("\t\t\tDevice allows writing on immediate state sessions and quick formatting\n");
+ printf("\t\t\tDevice does %ssupport blanking of media\n", (pos[0] & 1)?"":"NOT ");
+ break;
+ case 0x002d:
+ printf("\t\tTrack at once recording support\n");
+ datablock_types = pos[3] | (pos[2] << 8);
+ if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
+ if (pos[0] & 16) printf("\t\t\tDevice is capable of writing R-W Sub code in RAW mode\n");
+ if (pos[0] & 8) printf("\t\t\tDevice is capable of writing R-W Sub code in Packet mode\n");
+ printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
+ if (pos[0] & 2) printf("\t\t\tDevice supports overwriting a track using track at once\n");
+ if (pos[0] & 1) printf("\t\t\tDevice supports writing R-W Sub channels with user data\n");
+ printf("\t\t\tTrack at once data types supported (bitfield) 0x%04x\n", datablock_types);
+ break;
+ case 0x002e:
+ printf("\t\tSession at once or RAW writing support; CD Mastering\n");
+ cuesheet_len = pos[3] | (pos[2] << 8) | (pos[1] << 16);
+ if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
+ printf("\t\t\tDevice can %swrite using the Session at Once write type\n", (pos[0] & 32)?"":"CANT ");
+ if (pos[0] & 16) printf("\t\t\tDevice is capable of writing multi-session in RAW mode\n");
+ printf("\t\t\tDevice can %swrite using the raw write type\n", (pos[0] & 8)?"":"CANT ");
+ printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
+ if (pos[0] & 2) printf("\t\t\tDevice supports overwriting previous recorded media\n");
+ if (pos[0] & 1) printf("\t\t\tDevice supports writing R-W Sub channels with user data\n");
+ printf("\t\t\tMaximum cue sheet length %d bytes\n", cuesheet_len);
+ break;
+ case 0x002f:
+ printf("\t\tDVD-R/-RW Write; The ability to write DVD specific structures\n");
+ if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
+ printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
+ if (pos[0] & 2) printf("\t\t\tDevice supports overwriting previous recorded media\n");
+ break;
+ case 0x0030:
+ printf("\t\tCan read DDCD user data\n");
+ break;
+ case 0x0031:
+ printf("\t\tCan write and read DDCD-R media\n");
+ printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
+ break;
+ case 0x0032:
+ printf("\t\tCan write and read DDCD-RW media\n");
+ if (pos[0] & 2) printf("\t\t\tDevice allows writing on immediate state sessions and quick formatting\n");
+ printf("\t\t\tDevice does %ssupport blanking of media\n", (pos[0] & 1)?"":"NOT ");
+ break;
+ case 0x0033:
+ printf("\t\tLayer jump recording feature\n");
+ num_link_sizes = pos[3];
+ printf("\t\t\tDevice supports %d link size%s :", num_link_sizes, (num_link_sizes != 1)?"s":"");
+ for (cnt=0; cnt < num_link_sizes; cnt++) {
+ printf(" %d", pos[4+cnt]);
+ }
+ printf("\n");
+ break;
+ case 0x0037:
+ printf("\t\tCD-RW Media Write Support\n");
+ printf("\t\t\tCan %swrite media subtype 0\n", (pos[1] & 1) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 1\n", (pos[1] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 2\n", (pos[1] & 4) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 3\n", (pos[1] & 8) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 4\n", (pos[1] & 16) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 5\n", (pos[1] & 32) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 6\n", (pos[1] & 64) ? "": "NOT ");
+ printf("\t\t\tCan %swrite media subtype 7\n", (pos[1] & 128) ? "": "NOT ");
+ break;
+ case 0x0038:
+ printf("\t\tBD-R Pseudo-Overwrite (POW) support feature\n");
+ break;
+ case 0x003b:
+ printf("\t\tDVD+R Double Layer support\n");
+ printf("\t\t\tDrive can read DVD+R double layer discs\n");
+ printf("\t\t\tDrive can %swrite DVD+R double layer discs (only valid when active)\n", (pos[1] & 1) ? "" : "NOT ");
+ break;
+ case 0x0040:
+ printf("\t\tBluRay disc formats read support :\n");
+ printf("\t\t\tAble to read control structures and user data from the BD disc\n");
+ printf("\t\t\tDrive does %ssupport reporting disc BCA data\n", (pos[0] & 1)?"":"NOT ");
+ printf("\t\t\tCan %sread BD-ROM media\n", (pos[21] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %sread BD-R media\n", (pos[13] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %sread BD-RE media version 1\n", (pos[5] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %sread BD-RE media version 2\n", (pos[5] & 4) ? "": "NOT ");
+ if (feature_ver == 0)
+ printf("\t\t\tOther obsolete BD version read flags not dumped\n");
+ break;
+ case 0x0041:
+ printf("\t\tBluRay disc formats write support :\n");
+ printf("\t\t\twrite command does %ssupport Verify-Not-Required (VNR) bit\n", (pos[0] & 1)?"":"NOT ");
+ printf("\t\t\tCan %swrite BD-R media\n", (pos[13] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %swrite BD-RE media version 1\n", (pos[5] & 2) ? "": "NOT ");
+ printf("\t\t\tCan %swrite BD-RE media version 2\n", (pos[5] & 4) ? "": "NOT ");
+ break;
+ case 0x0100:
+ printf("\t\tPower management support\n");
+ break;
+ case 0x0101:
+ printf("\t\tS.M.A.R.T. (Self-Monitoring Analysis and Reporting Technology support)\n");
+ printf("\t\t\tDevice does %sprovide Fault/Failure Reporting Mode Page\n", (pos[0] & 1)?"":"NOT ");
+ break;
+ case 0x0102:
+ printf("\t\tEmbedded changer feature\n");
+ printf("\t\t\tDevice is %scapable of switching media sides\n", (pos[0] & 16)?"":"NOT ");
+ printf("\t\t\tDevice can %sreport disc presence after a reset of magazine change\n", (pos[0] & 4)?"":"NOT ");
+ break;
+ case 0x0103:
+ printf("\t\tAbility to play audio CDs via the Logical Unit's own analog output\n");
+ num_vol_levels = pos[3] | (pos[2] << 8);
+ printf("\t\t\tDevice does %ssupport the SCAN command\n", (pos[0] & 4)?"":"NOT ");
+ printf("\t\t\tDevice %s mute separate channels\n", (pos[0] & 4)?"can":"can't");
+ printf("\t\t\tDevice %s set volumes for each channel separately\n", (pos[0] & 4)?"can":"can't");
+ printf("\t\t\tDevice has %d audio volume levels\n", num_vol_levels);
+ break;
+ case 0x0104:
+ printf("\t\tAbility for the device to accept new microcode via the interface\n");
+ break;
+ case 0x0105:
+ printf("\t\tAbility to respond to all commands within a specific time\n");
+ break;
+ case 0x0106:
+ printf("\t\tAbility to perform DVD CSS/CPPM authentication and RPC\n");
+ printf("\t\t\tSupporting CCS version %d\n", pos[3]);
+ break;
+ case 0x0107:
+ printf("\t\tAbility to read and write using Initiator requested performance parameters; realtime streaming\n");
+ printf("\t\t\tDevice does %shave the `read buffer capacity' command \n", (pos[0] & 16)?"":"NOT ");
+ printf("\t\t\tDevice CD speed can %sbe set up\n", (pos[0] & 8)?"":"NOT ");
+ printf("\t\t\tDevice write speed performance can %sbe queried\n", (pos[0] & 4)?"":"NOT ");
+ printf("\t\t\tDevice does %shave a `set streaming' command\n", (pos[0] & 2)?"":"NOT ");
+ printf("\t\t\tDevice does %ssupport stream recording operation\n", (pos[0] & 1)?"":"NOT ");
+ break;
+ case 0x0108:
+ printf("\t\tThis device has an unique serialnumber : \"");
+ for (cnt=0; cnt < feature_len; cnt++) {
+ printf("%c", pos[cnt]);
+ }
+ printf("\"\n");
+ break;
+ case 0x010a:
+ printf("\t\tThe ability to read and/or write Disc Control Blocks (DVD only)\n");
+ for (cnt=0; cnt < feature_len; cnt+=4, pos+=4) {
+ dcb_entry = pos[3] | (pos[2] << 8) | (pos[1] << 16);
+ printf("\t\t\t\tSupported Disc Control Blocks content descriptor %d\n", dcb_entry);
+ }
+ printf("\n");
+ break;
+ case 0x010b:
+ printf("\t\tThe Logical Unit supports DVD CPRM authentication\n");
+ printf("\t\t\tSupporting CPRM version %d\n", pos[3]);
+ break;
+ case 0x010c:
+ printf("\t\tFirmware Information feature :\n");
+ century = (pos[ 1] - '0') + 10*(pos[ 0] - '0');
+ year = (pos[ 3] - '0') + 10*(pos[ 2] - '0');
+ month = (pos[ 5] - '0') + 10*(pos[ 4] - '0');
+ day = (pos[ 7] - '0') + 10*(pos[ 6] - '0');
+ hour = (pos[ 9] - '0') + 10*(pos[ 8] - '0');
+ minute = (pos[11] - '0') + 10*(pos[10] - '0');
+ second = (pos[13] - '0') + 10*(pos[12] - '0');
+ printf("\t\t\tFirmware version date %d-%d-%2d%02d at time %02d:%02d:%02d\n", day, month, century, year, hour, minute, second);
+ break;
+ case 0x010d:
+ printf("\t\tAACS feature :\n");
+ printf("\t\t\tDrive does %ssupports reading the drive certificate (RDC)\n", (pos[0] & 16)?"":"NOT ");
+ printf("\t\t\tDrive does %ssupports reading the media key block (RMC)\n", (pos[0] & 8)?"":"NOT ");
+ printf("\t\t\tDrive does %ssupport writing sectors with bus encription (WBE)\n", (pos[0] & 4)?"":"NOT ");
+ printf("\t\t\tDrive does %ssupport bus encryption (BEC)\n", (pos[0] & 2)?"":"NOT ");
+ printf("\t\t\tDrive does %ssupport generating the Binding-Nonce (%d blocks required)\n", (pos[0] & 1)?"":"NOT ", pos[1]);
+ printf("\t\t\tDrive supports maximal %d AGID's concurrently\n", pos[2] & 15);
+ printf("\t\t\tAACS version supported v %d\n", pos[3]);
+ break;
+ case 0x01ff:
+ printf("\t\tFirmware creation date report :\n");
+ century = pos[ 1] | (pos[ 0] << 8);
+ year = pos[ 3] | (pos[ 2] << 8);
+ month = pos[ 5] | (pos[ 4] << 8);
+ day = pos[ 7] | (pos[ 6] << 8);
+ hour = pos[ 9] | (pos[ 8] << 8);
+ minute = pos[11] | (pos[10] << 8);
+ printf("\t\t\tFirmware version date %d-%d-%2d%2d at time %02d:%02d\n", day, month, century, year, hour, minute);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+}
+
+
+#define max_feat_tbl_len 64*1024 /* don't change */
+void dump_drive_configuration(uint32_t feature_current) {
+ scsicmd cmd;
+ uint8_t features[max_feat_tbl_len], *fpos;
+ uint32_t feature, last_feature, features_len, feat_tbl_len;
+ uint32_t current_profile, pos;
+ uint32_t feature_cur, feature_len;
+ int error;
+
+ printf("Getting drive configuration\n");
+
+ /* get total length */
+ last_feature = feature = 0;
+ feat_tbl_len = 8;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x46; /* Get configuration */
+ cmd[1] = 0; /* RT=0 -> all independent of current setting */
+ cmd[2] = (last_feature) >> 8; /* MSB feature number */
+ cmd[3] = (last_feature) & 0xff; /* LSB feature number */
+ cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */
+ cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, features, feat_tbl_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading feature table length : %s\n", strerror(error));
+ return;
+ }
+ features_len = features[3] | (features[2]<<8) | (features[1]<<16) | (features[0]<<24);
+ current_profile = features[7] | (features[6]<<8);
+
+
+ printf("\tTotal features table length %d bytes\n", features_len);
+ printf("\tCurrent profile 0x%04x `%s'\n", current_profile, print_mmc_profile(current_profile));
+
+ /* getting feature table size */
+ last_feature = feature = 0;
+ do {
+ feat_tbl_len = 8;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x46; /* Get configuration */
+ cmd[1] = 0; /* RT=0 -> all independent of current setting */
+ cmd[2] = (last_feature) >> 8; /* MSB feature number */
+ cmd[3] = (last_feature) & 0xff; /* LSB feature number */
+ cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */
+ cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, features, feat_tbl_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading feature table length : %s\n", strerror(error));
+ return;
+ }
+
+ /* actually request them */
+ feat_tbl_len = features[3] | (features[2]<<8) | (features[1]<<16) | (features[0]<<24);
+ feat_tbl_len = MIN(max_feat_tbl_len, feat_tbl_len);
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x46; /* Get configuration */
+ cmd[1] = 0; /* RT=0 -> all independent of current setting */
+ cmd[2] = (last_feature) >> 8; /* MSB feature number */
+ cmd[3] = (last_feature) & 0xff; /* LSB feature number */
+ cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */
+ cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, features, feat_tbl_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading feature table : %s\n", strerror(error));
+ return;
+ }
+
+ pos = 8;
+ while (pos < feat_tbl_len) {
+ fpos = &features[pos];
+
+ feature = fpos[1] | (fpos[0] << 8);
+ feature_cur = (fpos[2] & 1);
+ feature_len = fpos[3];
+ if (feature_cur == feature_current) dump_feature(fpos);
+
+ last_feature = MAX(last_feature, feature);
+ if ((feature_len & 3) != 0) {
+ printf("\t\t*** drive returned bad feature length ***\n");
+ dump_feature(fpos);
+ feature_len = (feature_len + 3) & ~3;
+ }
+ pos += 4 + feature_len;
+ }
+ if (feat_tbl_len >= 0xffff)
+ printf("WARNING: requesting 2nd chunk, not tested\n");
+ } while (feat_tbl_len >= 0xffff);
+}
+#undef max_feat_tbl_len
+
+
+#define max_di_len 1000
+#define max_ti_len 128
+void dump_disc_information(void) {
+ scsicmd cmd;
+ uint8_t di[max_di_len], ti[max_ti_len];
+ int di_len = max_di_len, ti_len = max_ti_len;
+ int disc_status, disc_type, last_session_state, eraseable;
+ int num_sessions;
+ int first_track, first_track_last_session, last_track_last_session;
+ int data_length, is_track_number, is_session_number, is_track_mode, is_data_mode;
+ int is_copy, is_damage, is_fixed_packet, is_packet_or_inc, is_blank, is_reserved;
+ int nwa_valid, lra_valid;
+ uint32_t track_start, next_writable_addr, free_blocks, packet_size, track_size, last_recorded_addr;
+ uint32_t disc_id, disc_barcode_l, disc_barcode_h;
+ uint64_t disc_barcode;
+ int track, speed, pos, num_opc_tables;
+ int printed, error;
+
+ bzero(di, di_len);
+ bzero(ti, ti_len);
+
+ /* read in fixed part */
+ di_len = 34;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x51; /* Read disc information */
+ cmd[7] = 0; /* MSB allocation length */
+ cmd[8] = di_len; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, di, di_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading disc information : %s\n", strerror(error));
+ return;
+ }
+
+ di_len = di[1] | (di[0]<<8);
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x51; /* Read disc information */
+ cmd[7] = di_len >> 8; /* MSB allocation length */
+ cmd[8] = di_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, di, di_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading disc information : %s\n", strerror(error));
+ return;
+ }
+
+ di_len = di[1] | (di[0]<<8);
+ printf("\tDrive returned %d bytes of data\n", di_len);
+
+ disc_type = di[ 8];
+ disc_status = di[ 2] & 3;
+ last_session_state = (di[ 2] >> 2) & 3;
+ eraseable = di[ 2] & 16;
+ first_track = di[ 3];
+ num_sessions = di[ 4] | (di[ 9] << 8);
+ first_track_last_session = di[ 5] | (di[10] << 8);
+ last_track_last_session = di[ 6] | (di[11] << 8);
+ disc_id = di[15] | (di[14]<<8) | (di[13]<<16) | (di[12]<<24);
+ disc_barcode_l = di[31] | (di[30]<<8) | (di[29]<<16) | (di[28]<<24);
+ disc_barcode_h = di[27] | (di[26]<<8) | (di[25]<<16) | (di[24]<<24);
+ disc_barcode = ((uint64_t) disc_barcode_h << 32) | disc_barcode_l;
+ num_opc_tables = di[33];
+
+ if (di_len < 33) num_opc_tables = 0;
+
+ printf("\tDisc type : %s\n", print_disc_type(disc_type));
+ printf("\tDisc status : %s\n", print_disc_state(disc_status));
+ printf("\tKind of disc : %s\n", eraseable ? "eraseable (rewritable disc)": "NOT eraseable (recordable disc)");
+ printf("\tFirst track number : %d\n", first_track);
+ printf("\tNumber of sessions : %d\n", num_sessions);
+ printf("\tDrive does %ssupport setting OPC information; num tables %d\n", num_opc_tables ? "": "NOT ", num_opc_tables);
+ printf("\tLast session information :\n");
+ printf("\t\tState : %s\n", print_session_state(last_session_state));
+ printf("\t\tFrom track %d to track %d (including hidden)\n", first_track_last_session, last_track_last_session);
+ printf("\tDisc is %sin 'Restricted Use Mode'\n", (di[7] & 32) ? "": "NOT ");
+ switch (di[7] & 3) {
+ case 0 : printf("\tBackground formatting not applicable"); break;
+ case 1 : printf("\tDisc formatting was interrupted and not running now"); break;
+ case 2 : printf("\tDisc is being formatted in the background"); break;
+ case 3 : printf("\tDisc is formatted"); break;
+ }
+ printf("\n");
+ printf("\tDisc has %s valid 'Disc ID' : (0x%08"PRIx32")\n", (di[7] & 128) ? "a ": "NO", disc_id);
+ printf("\tDisc has %s valid 'Disc Bar Code' : (0x%016"PRIx64")\n", (di[7] & 64) ? "a ": "NO", disc_barcode);
+ printf("\tDisc has %s valid 'Disc Application code' : (0x%x)\n", (di[7] & 16) ? "a" : "NO", di[32]);
+ printf("\tLast Session Lead-in Start Address not dumped\n");
+ printf("\tLast Possible Lead-out Start Address not dumped\n");
+
+ printf("\n");
+ if (di_len < 33) {
+ printf("\tDrive reported no recording speeds and OPC data\n");
+ } else {
+ printf("\tRecording speeds (in kB/sec) (OPC data not dumped) :\n\t\t");
+ pos = 34;
+ printed = 0;
+ while (pos < di_len) {
+ speed = di[pos+1] | (di[pos] << 8);
+ if (speed) {
+ printed = 1;
+ printf("%d", speed);
+ if (pos < di_len-8) printf(", ");
+ }
+ pos += 8;
+ }
+ if (!printed)
+ printf("\t\tAparently no speeds were returned");
+ printf("\n");
+ }
+
+ printf("\nReading track and session information; track by track\n");
+ for (track = first_track; track <= last_track_last_session; track++) {
+ ti_len = 39 + 1; /* make ATAPI happy */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ bzero(ti, ti_len);
+ cmd[0] = 0x52; /* Read track information */
+ cmd[1] = 1; /* indexed on track */
+ cmd[4] = track >> 8; /* track number 0-0xff ? */
+ cmd[5] = track & 0xff;
+ cmd[7] = ti_len >> 8;
+ cmd[8] = ti_len & 0xff;
+ cmd[9] = 0;
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, ti, ti_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading track info : %s\n", strerror(error));
+ break;
+ }
+ data_length = ti[1] | (ti[0] << 8);
+ is_track_number = ti[2] | (ti[32] << 8);
+ is_session_number = ti[3] | (ti[33] << 8);
+ is_track_mode = ti[5] & 15;
+ is_copy = ti[5] & 16;
+ is_damage = ti[5] & 32;
+ is_data_mode = ti[6] & 15;
+ is_fixed_packet = ti[6] & 16;
+ is_packet_or_inc = ti[6] & 32;
+ is_blank = ti[6] & 64;
+ is_reserved = ti[6] & 128;
+ is_data_mode = ti[6] & 15;
+ nwa_valid = ti[7] & 1;
+ lra_valid = ti[7] & 2;
+ track_start = ti[11] | (ti[10]<<8) | (ti[ 9]<<16) | (ti[ 8]<<24);
+ next_writable_addr = ti[15] | (ti[14]<<8) | (ti[13]<<16) | (ti[12]<<24);
+ free_blocks = ti[19] | (ti[18]<<8) | (ti[17]<<16) | (ti[16]<<24);
+ packet_size = ti[23] | (ti[22]<<8) | (ti[21]<<16) | (ti[20]<<24);
+ track_size = ti[27] | (ti[26]<<8) | (ti[25]<<16) | (ti[24]<<24);
+ last_recorded_addr = ti[31] | (ti[30]<<8) | (ti[29]<<16) | (ti[28]<<24);
+
+ if (data_length <= 30) {
+ /* 8 bits track and session numbers returned; last_recordable is invalid */
+ is_track_number &= 0xff;
+ is_session_number &= 0xff;
+ last_recorded_addr = 0;
+ }
+ printf("\tTrack %d of session %d\n", is_track_number, is_session_number);
+ if (data_length < 27 && lra_valid) printf("\t\tLast recorded addres valid but not returned\n");
+ printf("\t\tStart at : %d\n", track_start);
+ printf("\t\tLength : %d\n", track_size);
+ printf("\t\tTrackmode : %s\n", print_Q_control(is_track_mode));
+ printf("\t\tDatamode : %s\n", (is_data_mode == 1) ? "mode 1" : "mode 2");
+ if (free_blocks) printf("\t\tFree blocks : %d\n", free_blocks);
+ if (packet_size) printf("\t\tPacket size : %d\n", packet_size);
+ if (nwa_valid) printf("\t\tNext writable : %d\n", next_writable_addr);
+ if (lra_valid) printf("\t\tLast recorded : %d\n", last_recorded_addr);
+ if (is_copy) printf("\t\tTrack is a copy\n");
+ if (is_reserved) printf("\t\tReserved or Complete track\n");
+ if (is_blank) printf("\t\tBlank track\n");
+ if (is_packet_or_inc) printf("\t\tPacket mode track\n");
+ if (is_fixed_packet) printf("\t\tFixed packet sizes track\n");
+
+ if (data_length > ti_len) {
+ printf("\t\tRest %d bytes undumed\n", data_length - ti_len);
+ }
+ }
+}
+#undef di_len
+#undef ti_len
+
+
+void dump_format_capabilities(void) {
+ scsicmd cmd;
+ uint8_t buf[512], *fcd;
+ uint32_t num_blks, param;
+ char *format_str, *nblks_str, *param_str;
+ int dscr_type, list_length, format_type;
+ int buf_len = 512, trans_len;
+ int error;
+
+ bzero(buf, buf_len);
+
+ trans_len = 12; /* only fixed header first */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x23; /* Read format capabilities */
+ cmd[7] = trans_len >> 8; /* MSB allocation length */
+ cmd[8] = trans_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, trans_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading format capabilities : %s\n", strerror(error));
+ return;
+ }
+
+ list_length = buf[ 3];
+
+ printf("\tCurrent/max capacity followed by additional capacity, reported length of %d bytes (8/entry)\n", list_length);
+ if (list_length % 8) {
+ printf("\t\tWarning: violating SCSI spec, capacity list length ought to be multiple of 8\n");
+ printf("\t\tInterpreting as including header of 4 bytes\n");
+ assert(list_length % 8 == 4);
+ list_length -= 4;
+ }
+
+ /* read in full capacity list */
+ trans_len = 12 + list_length; /* complete structure */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x23; /* Read format capabilities */
+ cmd[7] = trans_len >> 8; /* MSB allocation length */
+ cmd[8] = trans_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, trans_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading format capabilities : %s\n", strerror(error));
+ return;
+ }
+
+ fcd = buf + 4;
+ list_length -= 4; /* seems to include the first entry */
+ while (list_length > 0) {
+ num_blks = fcd[ 3] | (fcd[ 2] << 8) | (fcd[ 1] << 16) | (fcd[ 0] << 24);
+ dscr_type = fcd[ 4] & 3;
+ format_type = fcd[ 4] >> 2;
+ param = fcd[ 7] | (fcd[ 6] << 8) | (fcd[ 5] << 16);
+
+ format_str = nblks_str = param_str = "reserved";
+ switch (format_type) {
+ case 0x00 :
+ format_str = "full format capacity (non packet)";
+ nblks_str = "sectors";
+ param_str = "block length in bytes";
+ break;
+ case 0x01 :
+ format_str = "spare area expansion";
+ nblks_str = "extension in blocks";
+ param_str = "block length in bytes";
+ break;
+ /* 0x02 - 0x03 reserved */
+ case 0x04 :
+ format_str = "variable length zone'd format";
+ nblks_str = "zone length";
+ param_str = "zone number";
+ break;
+ case 0x05 :
+ format_str = "fixed length zone'd format";
+ nblks_str = "zone length";
+ param_str = "last zone number";
+ break;
+ /* 0x06 - 0x0f reserved */
+ case 0x10 :
+ format_str = "CD-RW/DVD-RW full packet format";
+ nblks_str = "adressable blocks";
+ param_str = "fixed packet size/ECC blocksize in sectors";
+ break;
+ case 0x11 :
+ format_str = "CD-RW/DVD-RW grow session";
+ nblks_str = "adressable blocks";
+ param_str = "fixed packet size/ECC blocksize in sectors";
+ break;
+ case 0x12 :
+ format_str = "CD-RW/DVD-RW add session";
+ nblks_str = "adressable blocks";
+ param_str = "maximum fixed packet size/ECC blocksize in sectors";
+ break;
+ case 0x13 :
+ format_str = "DVD-RW max growth of last complete session";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ break;
+ case 0x14 :
+ format_str = "DVD-RW max quick grow last session";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ break;
+ case 0x15 :
+ format_str = "DVD-RW quick full format";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ break;
+ /* 0x16 - 0x23 reserved */
+ case 0x24 :
+ format_str = "MRW format";
+ nblks_str = "Defect Management Area blocks";
+ param_str = "not used";
+ break;
+ /* 0x25 reserved */
+ case 0x26 :
+ format_str = "DVD+RW full format";
+ nblks_str = "sectors";
+ param_str = "not used";
+ break;
+ /* 0x27 - 0x2f reserved */
+ case 0x30 :
+ format_str = "BD-RE full format with spare area";
+ nblks_str = "blocks";
+ param_str = "total spare area size in clusters";
+ break;
+ case 0x31 :
+ format_str = "BD-RE full format without spare area";
+ nblks_str = "blocks";
+ param_str = "block length in bytes";
+ break;
+ /* 0x32 - 0x3f reserved */
+ default :
+ break;
+ }
+ printf("\n\tFormat type 0x%02x : %s\n", format_type, format_str);
+
+ switch (dscr_type) {
+ case 1 : printf("\t\tUnformatted media, maximum formatted capacity\n"); break;
+ case 2 : printf("\t\tFormatted media, current formatted capacity\n"); break;
+ case 3 : printf("\t\tNo media present or incomplete session, maximum formatted capacity\n"); break;
+ default : printf("\t\tUnspecified descriptor type\n"); break;
+ }
+ printf("\t\tNumber of blocks : %12d\t(%s)\n", num_blks, nblks_str);
+ printf("\t\tParameter : %12d\t(%s)\n", param, param_str);
+
+ fcd += 8;
+ list_length-=8;
+ }
+ printf("\n\tNote: last entry might be all zero due to deviation from SCSI standard\n");
+}
+
+
+void dump_formatted_toc(void) {
+ scsicmd cmd;
+ uint8_t toc[10000];
+ uint32_t lba;
+ int pos, toc_len, data_length;
+ int cur_track, addr, tno, cntrl;
+ int first_track, last_track;
+ int min, sec, frame;
+ int error;
+
+ printf("\nFormatted TOC\n");
+
+ /* only fixed part first */
+ toc_len = 4;
+
+ /* just TOC */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* return HMSF */
+ cmd[2] = 0; /* format 0; formatted TOC */
+ cmd[6] = 1; /* start at first track */
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "TOC reading of tracks failed : %s\n", strerror(error));
+ return;
+ }
+
+ first_track = read_cd_hex2(toc[2]);
+ last_track = read_cd_hex2(toc[3]);
+
+ printf("\tFirst track : %d\n", first_track);
+ printf("\tLast track : %d\n", last_track);
+ printf("\n");
+
+ /* complete formatted TOC */
+ data_length =toc[1] | (toc[0] << 8);
+ toc_len = data_length + 2;
+
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "TOC reading of complete formatted TOC failed : %s\n", strerror(error));
+ return;
+ }
+
+ /* dump formatted TOC */
+ data_length =toc[1] | (toc[0] << 8);
+ pos = 4; cur_track = 0;
+ while (pos < data_length + 2) {
+ cntrl = (toc[pos + 1] ) & 15;
+ addr = (toc[pos + 1] >> 4) & 15;
+ tno = read_cd_bcd(toc[pos + 2]);
+ /* hour = read_cd_bsd(toc[pos + 4]); symetry, though mandatory zero ? */
+ min = read_cd_bcd(toc[pos + 5]);
+ sec = read_cd_bcd(toc[pos + 6]);
+ frame = read_cd_bcd(toc[pos + 7]);
+
+ lba = cd_msf2lba(0, min, sec, frame);
+
+ if (tno != cur_track) {
+ if (tno == 0xAA) {
+ printf("\tLead-out area start");
+ } else {
+ printf("\tTrack %d start\t", tno);
+ }
+ }
+ cur_track = tno;
+
+ printf("\t%02d:%02d:%02d (LBA %8d)\t", min, sec, frame, lba);
+ printf(" (%s), ", print_Q_control(cntrl));
+
+ printf("Q sub-channel ");
+ switch (addr) {
+ case 0x0 : printf("not supplied"); break;
+ case 0x1 : printf("encodes current position data"); break;
+ case 0x2 : printf("encodes media catalog number"); break;
+ case 0x3 : printf("encodes ISRC"); break;
+ default : printf("encoding marked for reserved type (%d)", addr);
+ }
+ printf("\n");
+
+ pos += 8;
+ }
+}
+
+
+void dump_raw_toc_pma_data(uint8_t *toc) {
+ int pos, data_length;
+ int cur_session, session, cntrl, addr, tno, point;
+ int min, sec, frame, pmin, psec, pframe, nwa, lba, extent;
+
+ data_length = toc[1] | (toc[0] << 8);
+ pos = 4; cur_session = 0;
+ while (pos < data_length + 2) {
+ session = read_cd_bcd(toc[pos+ 0]);
+ cntrl = toc[pos+1] & 15;
+ addr = toc[pos+1] >> 4;
+ tno = read_cd_bcd(toc[pos+ 2]);
+ point = read_cd_bcd(toc[pos+ 3]);
+ min = read_cd_bcd(toc[pos+ 4]);
+ sec = read_cd_bcd(toc[pos+ 5]);
+ frame = read_cd_bcd(toc[pos+ 6]);
+ pmin = read_cd_bcd(toc[pos+ 8]);
+ psec = read_cd_bcd(toc[pos+ 9]);
+ pframe = read_cd_bcd(toc[pos+10]);
+ if (session != cur_session) {
+ if (session) printf("\tSession %d\n", session);
+ cur_session = session;
+ }
+ /* if (tno == 0 && session) { */
+ if (1) {
+ switch (addr) {
+ case 1 :
+ printf("\t\t");
+ switch (point) {
+ case 0xa0 :
+ printf("Disc type %s\n\t\t", print_TOC_disc_type(psec));
+ printf("First track number %d\t\t\t", pmin);
+ break;
+ case 0xa1 :
+ printf("Last track number %d\t\t\t", pmin);
+ break;
+ case 0xa2 :
+ lba = cd_msf2lba(0, pmin, psec, pframe);
+ printf("Lead out %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, lba);
+ break;
+ default :
+ lba = cd_msf2lba(0, pmin, psec, pframe);
+ printf("Track start %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, lba);
+ }
+ if (min || sec || frame)
+ printf("\n\t\tATIME (%02d:%02d.%02d)\t\t\t", min, sec, frame);
+ break;
+ case 2 :
+ printf("\t\t");
+ printf("Media catalog number present\n");
+ break;
+ case 3 :
+ printf("\t\t");
+ printf("International Standard Recording Code (ISRC) present\n");
+ break;
+ case 5 :
+ printf("\t\t");
+ switch (point) {
+ case 0xb0 :
+ nwa = cd_msf2lba(0, min, sec, frame);
+ extent = cd_msf2lba(0,pmin, psec, pframe);
+ printf("Next writable at %02d:%02d.%02d (LBA %8d)", min, sec, frame, nwa);
+ printf("\n\t\t");
+ printf("Lead out max at %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, extent);
+ /* printf("\n\t\t"); */
+ /* printf("Disc surface used for %2d %%\t\t", (int) (100.0 * (float) nwa / (float) extent)); */
+ break;
+ case 0xc0 :
+ printf("Start first lead-in %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, cd_msf2lba(0, pmin, psec, pframe));
+ printf("\n\t\t");
+ printf("Optimum recording power %d\t\t", min);
+ break;
+ case 0xc1 :
+ printf("Copy of A1 point from ATIP\t\t");
+ break;
+ default :
+ printf("<undumped %d, %d, %02x pair>\t\t", addr, cntrl, point);
+ break;
+ }
+ break;
+ default :
+ printf("\t\t<undumped %d, %d, %02x pair>\t\t", addr, cntrl, point);
+ break;
+ }
+ printf("\t (");
+ if ((cntrl & 12) == 4) {
+ printf("data track ");
+ if (cntrl & 1) printf("; incremental "); else printf("; uninterrupted");
+ } else {
+ printf("audio track");
+ if (cntrl & 1) printf("; pre-emphasis of 50/15 µs"); else printf("; no pre-emphasis");
+ }
+ if (cntrl & 2) printf("; copy prohibited");
+ printf(")\n");
+ }
+ pos += 11;
+ }
+}
+
+
+char *atip_speed(int speed) {
+ static char scrap[100];
+ switch (speed) {
+ case 0:
+ return "Reserved";
+ case 1:
+ return "2x";
+ case 2:
+ return "4x";
+ case 3:
+ return "8x";
+ case 4:
+ return "16x";
+ case 5:
+ return "32x";
+ case 6:
+ return "52x(?)";
+ case 7:
+ default:
+ sprintf(scrap, "(%d)", speed);
+ }
+ return scrap;
+}
+
+
+void dump_atip_data(uint8_t *toc) {
+ int pos, data_length;
+ int cur_session;
+ int writing_power, ref_speed, URU, disc_cdrw, disc_subtype, A1, A2, A3;
+ int lowest_clv_rec, highest_clv_rec;
+ int power_multi, target_y, rec_EW_ratio, hicap_cdr_min;
+ int min, sec, frame, pmin, psec, pframe;
+
+ data_length = toc[1] | (toc[0] << 8);
+ pos = 4; cur_session = 0;
+
+ writing_power = (toc[pos ] >> 4) & 15;
+ ref_speed = toc[pos ] & 7;
+ URU = toc[pos+ 1] & 64;
+ disc_cdrw = toc[pos+ 2] & 64;
+ disc_subtype = (toc[pos+ 2] >> 3) & 7;
+ A1 = toc[pos+ 2] & 4;
+ A2 = toc[pos+ 2] & 2;
+ A3 = toc[pos+ 2] & 1;
+ min = read_cd_bcd(toc[pos+ 4]);
+ sec = read_cd_bcd(toc[pos+ 5]);
+ frame = read_cd_bcd(toc[pos+ 6]);
+ pmin = read_cd_bcd(toc[pos+ 8]);
+ psec = read_cd_bcd(toc[pos+ 9]);
+ pframe = read_cd_bcd(toc[pos+10]);
+ lowest_clv_rec = (toc[pos+12] >> 4) & 7;
+ highest_clv_rec = (toc[pos+12] ) & 15;
+ power_multi = (toc[pos+13] >> 4) & 7;
+ target_y = (toc[pos+13] >> 1) & 7;
+ rec_EW_ratio = (toc[pos+14] >> 4) & 7;
+ hicap_cdr_min = toc[pos+14] & 15;
+
+ printf("\tS1 values\n");
+ printf("\t\tDisc type\t\t\t%s\n", disc_cdrw ? "CD-RW":"CD-R");
+ printf("\t\tDisc sub-type\t\t\t%d (media sensitivity/speed indicator)\n", disc_subtype);
+ printf("\t\tIndicative Target Writing Power\t%d\n", writing_power);
+ printf("\t\tDisc is%s in unrestricted use\n", URU ? "":" NOT");
+ printf("\t\tReference speed\t\t\t%s\n", atip_speed(ref_speed));
+ printf("\tS2 values\n");
+ printf("\t\tStart time of lead-in %02d:%02d.%02d (LBA %8d)\n", min, sec, frame, cd_msf2lba(0, min, sec, frame));
+ printf("\tS3 values\n");
+ if (hicap_cdr_min == 0) {
+ printf("\t\tLast possible lead-out %02d:%02d.%02d (LBA %8d)\n", pmin, psec, pframe, cd_msf2lba(0, pmin, psec, pframe));
+ } else {
+ printf("\t\tStart of hicap CD-R %02d:%02d.%02d (LBA %8d)\n", pmin, psec, pframe, cd_msf2lba(0, pmin, psec, pframe));
+ printf("\t\tExtra time in minutes %d (not sure if correct)\n", hicap_cdr_min);
+ printf("\t\tResulting last lead-out %02d:%02d.%02d (LBA %8d)\n", pmin + hicap_cdr_min, psec, pframe, cd_msf2lba(0, pmin + hicap_cdr_min, psec, pframe));
+ }
+ if (A1) {
+ printf("\tA1 values\n");
+ printf("\t\tLowest CLV speed\t%s\n", atip_speed(lowest_clv_rec));
+ printf("\t\tHighest CLV speed\t%s\n", atip_speed(highest_clv_rec));
+ printf("\t\tPower multiplication factor\t\t\t%d\n", power_multi);
+ printf("\t\tTarget y value of Modulation/Power function\t%d\n", target_y);
+ printf("\t\tRecommended erase/write power ration\t\t%d\n", rec_EW_ratio);
+ } else {
+ printf("\tA1 values are not valid\n");
+ printf("\t\tLowest and highest CLV speed information not specified\n");
+ printf("\t\tNo power recommendations\n");
+ }
+ if (A2) {
+ printf("\tA2 values\n");
+ printf("\t\t0x%02x 0x%02x 0x%02x\n", toc[pos+16], toc[pos+17], toc[pos+18]);
+ } else {
+ printf("\tA2 values not valid\n");
+ }
+ if (A3) {
+ printf("\tA3 values\n");
+ printf("\t\t0x%02x 0x%02x 0x%02x\n", toc[pos+20], toc[pos+21], toc[pos+22]);
+ } else {
+ printf("\tA3 values not valid\n");
+ }
+ if (data_length > 4+23) {
+ printf("\tS4 values\n");
+ printf("\t\t0x%02x 0x%02x 0x%02x\n", toc[pos+24], toc[pos+25], toc[pos+26]);
+ } else {
+ printf("\tS4 values not transfered\n");
+ }
+
+ printf("\n");
+}
+
+
+void dump_raw_toc_pma_atip_info(void) {
+ scsicmd cmd;
+ uint8_t toc[10000];
+ int toc_len;
+ int data_length;
+ int first_session;
+ int error;
+
+ printf("\nRaw TOC\n");
+ printf("\tLba numbers are indicative only\n");
+
+ /* return only fixed part or multi-session info */
+ toc_len = 4;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 0; /* LBA's preferably (not relevant) */
+ cmd[2] = 1; /* format 1; multi-session info */
+ cmd[7] = (toc_len >> 8) & 0xff;
+ cmd[8] = (toc_len ) & 0xff;
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "TOC reading of multi-session info failed : %s\n", strerror(error));
+ fprintf(stderr, "\tassuming first session is sesion 1\n");
+ first_session = 1;
+ } else {
+ /* XXX or just toc[2] ? */
+ first_session = read_cd_hex2(toc[2]);
+ }
+
+ /* return only fixed part of raw TOC */
+ toc_len = 4;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* officially no LBA's are defined */
+ cmd[2] = 2; /* format 2; raw TOC */
+ cmd[6] = first_session; /* start at first session */
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "TOC reading of fixed part of raw TOC failed : %s\n", strerror(error));
+ } else {
+ /* read in complete raw TOC */
+ data_length = toc[1] | (toc[0] << 8);
+ toc_len = data_length + 2; /* don't count length */
+
+ /* patch for ATAPI: make it even length */
+ if (toc_len & 1)
+ toc_len++;
+
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "TOC reading of complete raw TOC failed : %s\n", strerror(error));
+ } else {
+ dump_raw_toc_pma_data(toc);
+ }
+ }
+
+ printf("\nPMA\n");
+ printf("\tLba numbers are indicative only\n");
+
+ /* return only fixed part of PMA */
+ toc_len = 4;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* officially no LBA's are defined */
+ cmd[2] = 3; /* format 3; PMA */
+ cmd[6] = first_session; /* start at first session */
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "Reading of fixed part of PMA failed : %s\n", strerror(error));
+ } else {
+ /* read in complete PMA */
+ data_length =toc[1] | (toc[0] << 8);
+ toc_len = data_length + 2;
+
+ /* patch for ATAPI: make it even length */
+ if (toc_len & 1)
+ toc_len++;
+
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "Reading of complete PMA failed : %s\n", strerror(error));
+ } else {
+ dump_raw_toc_pma_data(toc);
+ }
+ }
+
+ printf("\nATIP\n");
+ printf("\tLba numbers are indicative only\n");
+
+ /* read in fixed part of response format 0100b */
+ toc_len = 4;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* officially no LBA's are defined */
+ cmd[2] = 4; /* format 4; ATIP */
+ cmd[6] = first_session; /* start at first session */
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "Reading of fixed part of ATIP addition information failed : %s\n", strerror(error));
+ } else {
+ /* read in complete format 0100b */
+ data_length =toc[1] | (toc[0] << 8);
+ toc_len = data_length + 4;
+
+ /* patch for ATAPI: make it even length */
+ if (toc_len & 1)
+ toc_len++;
+
+ cmd[7] = toc_len >> 8;
+ cmd[8] = toc_len & 0xff;
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "Reading of complete ATIP additional information failed : %s\n", strerror(error));
+ } else {
+ dump_atip_data(toc);
+ }
+ }
+}
+
+
+void dump_toc_pma_atip_info(void) {
+ dump_formatted_toc();
+ dump_raw_toc_pma_atip_info();
+}
+
+
+char *prchange(int val) {
+ if (val) return "[changeable]";
+ return "[fixed] ";
+}
+
+
+void dump_parameter_page(uint8_t *pos, uint8_t *change) {
+ int page_code, page_length;
+ int write_type, track_mode, data_block_type, link_size;
+ int testwrite, linksize_valid, zerolinking;
+ int appl_code, session_format, packet_size, audio_pause;
+ int multi_session, fixed_packets, copybit;
+ uint32_t recovery_time_limit, inactiv_time_mult, S_per_MSF, F_per_MSF;
+ uint32_t idle_timer_value, standby_timer_value, interval_timer_value;
+ uint32_t group1_timeouts, group2_timeouts;
+ int i, j, c, per_emcdr;
+
+ page_code = pos[0] & 63;
+ page_length = pos[1];
+
+ printf("\tGot page 0x%02x (%spersistent) : %2d bytes for ", page_code, pos[0] & 128? "":"non ", page_length);
+ switch (page_code) {
+ case 0x01 :
+ printf("read/write error recovery page\n");
+ printf("\t\tError recovery behaviour %s %d\n", prchange(change[2]), pos[2]);
+ printf("\t\t\tAutomatic Write Reallocation Enabled (AWRE) %s %s\n", prchange(change[2] & 128), pos[2] & 128 ? "On":"Off");
+ printf("\t\t\tAutomatic Read Reallocation Enabled (ARRE) %s %s\n", prchange(change[2] & 64), pos[2] & 64 ? "On":"Off");
+ printf("\t\tError recovery parameter %s %d\n", prchange(change[2] & 63), pos[2] & 63);
+ printf("\t\t\tTransfer Block (TB) %s %s\n", prchange(change[2] & 32), pos[2] & 32 ? "On":"Off");
+ printf("\t\t\tRead Continuous (RC) %s %s\n", prchange(change[2] & 16), pos[2] & 16 ? "On":"Off");
+ printf("\t\t\t<Reserved> %s %s\n", prchange(change[2] & 8), pos[2] & 8 ? "On":"Off");
+ printf("\t\t\tPost Error (PER) %s %s\n", prchange(change[2] & 4), pos[2] & 4 ? "On":"Off");
+ printf("\t\t\tDisable Transfer on Error (DTE) %s %s\n", prchange(change[2] & 2), pos[2] & 2 ? "On":"Off");
+ printf("\t\t\tDisable Correction (DCR) %s %s\n", prchange(change[2] & 1), pos[2] & 1 ? "On":"Off");
+ printf("\t\tEnhanced Media Certification and Defect Reporting %s %d\n", prchange(change[7] & 3), pos[7] & 3);
+ per_emcdr = (pos[2] & 4) + (pos[7] & 3);
+ if (per_emcdr == 0)
+ printf("\t\t\tNo certification of medium on read operations and it will not report recovered errors\n");
+ if (per_emcdr >=1)
+ printf("\t\t\tCertification of medium on read and on verify operation enabled; ");
+ if (per_emcdr == 1)
+ printf("shall not report recovered error\n");
+ if (per_emcdr == 2)
+ printf("shall report recovered error or unrecovered error on verify operation.\n");
+ if (per_emcdr == 3)
+ printf("shall report recovered error or unrecovered error on read operation and verify operation\n");
+ if (per_emcdr == 4)
+ printf("shall report recovered error if higher lever error correction is used\n");
+ if (per_emcdr >= 5)
+ printf("shall report recovered error as RECOVERED ERROR/RECOVERED DATA - RECOMMEND REASSIGNMENT\n");
+ printf("\t\tRead retry count %s %d\n", prchange(change[3]), pos[3]);
+ printf("\t\tWrite retry count %s %d\n", prchange(change[8]), pos[8]);
+ recovery_time_limit = pos[11] + (pos[10] << 8);
+ printf("\t\tRecovery time limit %s %d\n", prchange(change[11]), recovery_time_limit);
+ printf("\n");
+ break;
+ case 0x03 :
+ printf("MRW mode page\n");
+ printf("\t\t`%s' LBA space selected\n", pos[3] & 1 ? "General Application Area" : "Defect Managed Area");
+ printf("\n");
+ break;
+ case 0x05 :
+ printf("write parameter page\n");
+ write_type = pos[2] & 15;
+ track_mode = pos[3] & 15;
+ data_block_type = pos[4] & 15;
+ testwrite = pos[2] & 16;
+ linksize_valid = pos[2] & 32;
+ zerolinking = pos[2] & 64;
+ multi_session = pos[3] >> 6;
+ fixed_packets = pos[3] & 32;
+ copybit = pos[3] & 16;
+ link_size = pos[5];
+ appl_code = pos[7] & 63;
+ session_format = pos[8];
+ packet_size = pos[13] + (pos[12] << 8) + (pos[11] << 16) + (pos[10] << 24);
+ audio_pause = pos[15] + (pos[14] << 8);
+
+ printf("\t\tWrite type %s %s\n", prchange(change[2] & 15), print_write_type(write_type));
+ printf("\t\tTrack mode %s %d (mode 1 Q subchannel control nibble)\n", prchange(change[3] & 15), track_mode);
+ printf("\t\tData block type %s %s\n", prchange(change[4] & 15), print_data_block_type(data_block_type));
+ printf("\t\tTestwriting %s %s\n", prchange(change[2] & 16), testwrite ? "On":"Off");
+ printf("\t\tLinksize %s %d sectors (%s)\n", prchange(change[5]), link_size, linksize_valid ? "valid": "not valid (7)");
+ printf("\t\tApplication code %s %d\n", prchange(change[7] & 63), appl_code);
+ printf("\t\tSession format %s %s\n", prchange(change[8]), print_session_format(session_format));
+ printf("\t\tUsing fixed packets %s %s\n", prchange(change[3] & 32), fixed_packets ? "On":"Off");
+ printf("\t\tCopybit %s %s\n", prchange(change[3] & 16), copybit ? "On":"Off");
+ printf("\t\tMulti session field %s %d\n", prchange(change[3] >> 6), multi_session);
+ printf("\t\tPacket size %s %d\n", prchange(change[13]), packet_size);
+ printf("\t\tAudio pause length %s %d\n", prchange(change[15]), audio_pause);
+ printf("\t\tMedia catalog number %s 0x", prchange(change[31]));
+ for (i = 31; i >= 16; i--) printf("%02x", pos[i]);
+ printf("\n");
+ printf("\t\tInitiational standard recording code %s 0x", prchange(change[47]));
+ for (i = 47; i >= 32; i--) printf("%02x", pos[i]);
+ printf("\n");
+ printf("\t\tSubheader byte 0 %s 0x%02x\n", prchange(change[48]), pos[48]);
+ printf("\t\tSubheader byte 1 %s 0x%02x\n", prchange(change[49]), pos[49]);
+ printf("\t\tSubheader byte 2 %s 0x%02x\n", prchange(change[50]), pos[50]);
+ printf("\t\tSubheader byte 3 %s 0x%02x\n", prchange(change[51]), pos[51]);
+ printf("\n");
+ break;
+ case 0x07 :
+ printf("verify error recovery mode page\n");
+ printf("\t\tNot dumped yet. (And not permitted for MM LUs)\n");
+ printf("\n");
+ break;
+ case 0x08 :
+ printf("caching mode page\n");
+ printf("\t\tWrite cache %s %s\n", prchange(change[2] & 4), pos[2] & 4 ? "enabled" : "disabled");
+ printf("\t\tRead cache %s %s\n", prchange(change[2] & 1), pos[2] & 1 ? "disabled" : "enabled");
+ printf("\n");
+ break;
+ case 0x0b :
+ printf("media types supported page\n");
+ printf("\t\tNot dumped yet. (And not permitted for MM LUs)\n");
+ printf("\n");
+ break;
+ case 0x0d :
+ printf("cd device parameters page\n");
+ inactiv_time_mult = pos[3] & 15;
+ S_per_MSF = pos[5] + (pos[4] << 8);
+ F_per_MSF = pos[7] + (pos[6] << 8);
+ printf("\t\tHold track inactivity time %s value %d (%s)\n", prchange(change[3] & 15), inactiv_time_mult, print_inactivity_time(inactiv_time_mult));
+ printf("\t\tSeconds per MSF %s %d\n", prchange(change[5]), S_per_MSF);
+ printf("\t\tFrames per MSF %s %d\n", prchange(change[7]), F_per_MSF);
+ printf("\n");
+ break;
+ case 0x0e :
+ printf("cd audio control page\n");
+ printf("\t\tStop on track crossing %s %s\n", prchange(change[ 2] & 2 ), pos[2] & 2 ? "On":"Off");
+ printf("\t\tCDDA output port 0 channel %s %d\n", prchange(change[ 8] & 31), pos[ 8] & 31);
+ printf("\t\tOutput port 0 volume %s %d\n", prchange(change[ 9] ), pos[ 9]);
+ printf("\t\tCDDA output port 1 channel %s %d\n", prchange(change[10] & 31), pos[10] & 31);
+ printf("\t\tOutput port 1 volume %s %d\n", prchange(change[11] ), pos[11]);
+ printf("\t\tCDDA output port 2 channel %s %d\n", prchange(change[12] & 31), pos[12] & 31);
+ printf("\t\tOutput port 2 volume %s %d\n", prchange(change[13] ), pos[13]);
+ printf("\t\tCDDA output port 3 channel %s %d\n", prchange(change[14] & 31), pos[14] & 31);
+ printf("\t\tOutput port 3 volume %s %d\n", prchange(change[15] ), pos[15]);
+ printf("\n");
+ break;
+ case 0x1a :
+ printf("power condition page\n");
+ idle_timer_value = pos[ 7] + (pos[ 6] << 8) + (pos[ 5] << 16) + (pos[ 4] << 24);
+ standby_timer_value = pos[11] + (pos[10] << 8) + (pos[ 9] << 16) + (pos[ 8] << 24);
+ printf("\t\tIdle timer activate %s %s\n", prchange(change[3] & 2 ), pos[3] & 2 ? "On":"Off");
+ printf("\t\tStandby timer active %s %s\n", prchange(change[3] & 1 ), pos[3] & 1 ? "On":"Off");
+ printf("\t\tIdle timer start value %s %d * 100ms\n", prchange(change[ 7] & 1 ), idle_timer_value);
+ printf("\t\tStandby timer start value %s %d * 100ms\n", prchange(change[11] & 1 ), standby_timer_value);
+ printf("\n");
+ break;
+ case 0x1c :
+ printf("informational exceptions control page (not checked yet)\n");
+ interval_timer_value = pos[ 7] + (pos[ 6] << 8) + (pos[ 5] << 16) + (pos[ 4] << 24);
+ printf("\t\tLogging %s %s\n", prchange(pos[2] & 1), pos[2] & 1 ? "standard" : "vendor specific");
+ printf("\t\tTest device failure notification generation %s %s\n", prchange(pos[2] & 4), pos[2] & 4 ? "On" : "Off");
+ printf("\t\tInterval timer if enabled %s %d * 100ms (?)\n", prchange(pos[7] & 1), interval_timer_value);
+ printf("\t\tRest undumped\n");
+ printf("\n");
+ break;
+ case 0x1d :
+ printf("timeout and protect page\n");
+ group1_timeouts = pos[7] + (pos[6] << 8);
+ group2_timeouts = pos[9] + (pos[8] << 8);
+ printf("\t\tTimeouts commands enabled %s %s\n", prchange(change[4] & 4), pos[4] & 4 ? "On":"Off");
+ printf("\t\tDisable device till powerd. %s %s\n", prchange(change[4] & 2), pos[4] & 2 ? "On":"Off");
+ printf("\t\tSoftware write protect %s %s\n", prchange(change[4] & 1), pos[4] & 1 ? "On":"Off");
+ if (page_length >= 8)
+ printf("\t\tGroup 1 minimum time outs %s %d\n", prchange(change[7]), group1_timeouts);
+ if (page_length >= 10)
+ printf("\t\tGroup 2 minimum time outs %s %d\n", prchange(change[9]), group2_timeouts);
+ printf("\n");
+ break;
+ case 0x2a :
+ printf("CD/DVD capabilities and mechanical status page\n");
+ printf("\t\tNot dumped yet\n");
+ printf("\n");
+ break;
+ default :
+ /* dump bytes in hex:char notation */
+ printf("Reserved/vendor specific\n");
+ printf("\t\t ");
+ for (j = 0; j < 16; j++) {
+ printf("%02x ", j);
+ }
+ printf(" ASCII\n");
+ for (i = 2; i < page_length+2; i+= 16) {
+ printf("\t\t%02x ", i-2);
+ for (j = 0; j < 16; j++) {
+ if (i+j < page_length+2) {
+ printf("%02x ", pos[i+j]);
+ } else {
+ printf(" ");
+ }
+ }
+ printf(": ");
+ for (j = 0; j < 16; j++) {
+ if (i+j < page_length+2) {
+ c = pos[i+j];
+ if (c < 32 || c == 127)
+ c = '.';
+ printf("%c", c);
+ } else {
+ printf(" ");
+ }
+ }
+ printf("\n");
+ }
+ printf("\n");
+ break;
+ }
+}
+
+
+#define max_blk_len 60000
+void dump_parameter_pages(void) {
+ scsicmd cmd;
+ int blk_len, val_length, changeable_length;
+ uint8_t val[max_blk_len], changeable[max_blk_len], zeros[256];
+ int voff, choff, vpage_code, vpage_length, chpage_code, chpage_length;
+ int error;
+
+ bzero(val, max_blk_len);
+ bzero(changeable, max_blk_len);
+ bzero(zeros, 256);
+
+ /* read the block size */
+ blk_len = 8;
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x5A; /* MODE SENSE (10) */
+ cmd[1] = 0; /* allow multiple blocks */
+ cmd[2] = 0x3F; /* return all current values */
+ cmd[7] = blk_len >> 8; /* MSB block length */
+ cmd[8] = blk_len & 0xff; /* LSB block length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, val, blk_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading all parameter pages : %s\n", strerror(error));
+ return;
+ }
+ val_length = val[1] | (val[0] << 8);
+ printf("\tSCSI mode sense for all pages returned %d bytes of data\n", val_length);
+
+ /* read the mode sense block */
+ blk_len = val_length;
+ cmd[7] = blk_len >> 8; /* MSB block length */
+ cmd[8] = blk_len & 0xff; /* LSB block length */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, val, blk_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading all parameter pages : %s\n", strerror(error));
+ return;
+ }
+
+ cmd[2] = 64 | 0x3F; /* get all changeable values */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, changeable, blk_len, 10000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading all changeable parameter pages : %s\n", strerror(error));
+ }
+ changeable_length = changeable[1] | (changeable[0] << 8);
+ printf("\tSCSI mode sense changeable parameters list returned %d bytes of data\n", changeable_length);
+
+ printf("\n");
+
+ /* read the mode sense block and process page by page */
+ voff = 8;
+ while (voff < val_length) {
+ vpage_code = val[voff ] & 63;
+ vpage_length = val[voff + 1];
+
+ if (vpage_code) {
+ choff = 8; chpage_code = -1;
+ while (choff < changeable_length) {
+ chpage_code = changeable[choff ] & 63;
+ chpage_length = changeable[choff + 1];
+ if (chpage_code == vpage_code) break;
+ choff += chpage_length + 2;
+ }
+ if (chpage_code == vpage_code) {
+ dump_parameter_page(val + voff, changeable + choff);
+ } else {
+ printf("\tWarning: no matching changeable bits page was found\n");
+ dump_parameter_page(val + voff, zeros);
+ }
+ }
+ voff += vpage_length + 2;
+ }
+}
+#undef max_blk_len
+
+
+int main(int argc, char **argv) {
+ scsicmd cmd;
+ uint8_t buf[36];
+ struct uscsi_addr saddr;
+ int drive_type;
+ int error;
+
+ bzero(&dev, sizeof(dev));
+
+ if (argc != 2) {
+ printf("Usage : %s devicename\n", argv[0]);
+ return 1;
+ }
+
+ /* Open the device */
+ dev.dev_name = strdup(argv[1]);
+ printf("Opening device %s\n", dev.dev_name);
+ if (uscsi_open(&dev) != 0) {
+ exit(1);
+ }
+
+ error = uscsi_check_for_scsi(&dev);
+ if (error) {
+ fprintf(stderr, "sorry, not a SCSI device : %s\n", strerror(error));
+ exit(1);
+ }
+
+ error = uscsi_identify(&dev, &saddr);
+ if (error) {
+ fprintf(stderr, "SCSI identify returned : %s\n", strerror(error));
+ exit(1);
+ }
+
+ printf("\n\nDevice attachment identifies itself as : ");
+
+ switch (saddr.type) {
+ case USCSI_TYPE_SCSI :
+ printf("SCSI busnum = %d, target = %d, lun = %d\n",
+ saddr.addr.scsi.scbus, saddr.addr.scsi.target, saddr.addr.scsi.lun);
+ break;
+ case USCSI_TYPE_ATAPI :
+ printf("ATAPI busnum = %d, drive = %d\n",
+ saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
+ break;
+ case USCSI_TYPE_UNKNOWN :
+ printf("Unknown attachment\n");
+ }
+
+ printf("\n\nTest unit ready\n");
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0; /* test unit ready */
+ error = uscsi_command(SCSI_READCMD, &dev, cmd, 6, buf, 0, 10000, NULL);
+ if (error) perror("SCSI test unit ready returned : ");
+
+ printf("Device identifies itself as : ");
+ dump_drive_identify(&drive_type);
+
+ printf("\n\nCURRENT features\n");
+ dump_drive_configuration(1);
+
+ printf("\n\nCAPAPLE features\n");
+ dump_drive_configuration(0);
+
+ printf("\n\nRead recorded capacity\n");
+ dump_recorded_capacity();
+
+ if (drive_type == DEVICE_TYPE_MMC) {
+ printf("\n\nDisc information\n");
+ dump_disc_information();
+ }
+
+ printf("\n\nFormat capabilities\n");
+ dump_format_capabilities();
+
+ if (drive_type == DEVICE_TYPE_MMC) {
+ printf("\n\nReading TOC/PMA/ATIP\n");
+ dump_toc_pma_atip_info();
+ }
+
+ if (drive_type == DEVICE_TYPE_MMC) {
+ /* normally support mode sense 10 */
+ printf("\n\nReading SCSI 'mode sense' parameter pages\n");
+ dump_parameter_pages();
+ } else {
+ printf("\n\nnot an MMC class, `mode sense' 10 parameters might fail\n");
+ dump_parameter_pages();
+ }
+
+ uscsi_close(&dev);
+
+ return 0;
+}
+
diff --git a/cd_sessions.c b/cd_sessions.c
new file mode 100644
index 0000000..d4a1707
--- /dev/null
+++ b/cd_sessions.c
@@ -0,0 +1,80 @@
+/* $NetBSD$ */
+
+/*
+ * File "cd_sessions.c" is part of the UDFclient toolkit.
+ * File $Id: cd_sessions.c,v 1.34 2011/02/01 20:43:40 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/types.h>
+
+#include "udf.h"
+#include "udf_bswap.h"
+
+
+struct udf_discinfo *disc;
+extern int udf_verbose;
+extern int uscsilib_verbose;
+
+
+extern void udf_dump_discinfo(struct udf_discinfo *disc);
+
+int main(int argc, char **argv) {
+ char *dev_name;
+ int error;
+
+ if (argc != 2) {
+ printf("Usage : %s devicename\n", argv[0]);
+ return 1;
+ }
+
+ dev_name = argv[1];
+ udf_verbose = UDF_VERBLEV_ACTIONS;
+ udf_verbose = UDF_VERBLEV_MAX;
+ uscsilib_verbose = 0;
+
+ printf("Opening device %s\n", dev_name);
+ error = udf_open_disc(dev_name, /* discop_flags */ 0, &disc);
+ if (error) {
+ fprintf(stderr, "Can't open my device : %s\n", strerror(error));
+ exit(1);
+ }
+
+ udf_dump_discinfo(disc);
+
+ udf_close_disc(disc);
+
+ return 0;
+}
+
diff --git a/config.guess b/config.guess
new file mode 100755
index 0000000..1e6f50b
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1463 @@
+#! /bin/sh
+#
+# $NetBSD: config.guess,v 1.8 2004/08/14 19:13:55 schmonz Exp $
+#
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-06-11'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amd64:OpenBSD:*:*)
+ echo x86_64-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ cats:OpenBSD:*:*)
+ echo arm-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ luna88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ # GNU/KFreeBSD systems have a "k" prefix to indicate we are using
+ # FreeBSD's kernel, but not the complete OS.
+ case ${LIBC} in gnu) kernel_only='k' ;; esac
+ echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.sub b/config.sub
new file mode 100755
index 0000000..b9e9bc4
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1555 @@
+#! /bin/sh
+#
+# $NetBSD: config.sub,v 1.7 2004/08/14 19:14:42 schmonz Exp $
+#
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-03-12'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m32rle | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | msp430-* \
+ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ cr16c)
+ basic_machine=cr16c-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nv1)
+ basic_machine=nv1-cray
+ os=-unicosmp
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..c4b89e3
--- /dev/null
+++ b/configure
@@ -0,0 +1,5162 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for udfclient 0.8.1.
+#
+# Report bugs to <reinoud@NetBSD.org>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and reinoud@NetBSD.org
+$0: about your system, including any error possibly output
+$0: before this message. Then install a modern shell, or
+$0: manually run the script under such a shell if you do
+$0: have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='udfclient'
+PACKAGE_TARNAME='udfclient'
+PACKAGE_VERSION='0.8.1'
+PACKAGE_STRING='udfclient 0.8.1'
+PACKAGE_BUGREPORT='reinoud@NetBSD.org'
+PACKAGE_URL=''
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+SCSI_LIB
+BUILD_APPS
+EGREP
+GREP
+CPP
+TIMELIB
+THREADLIB
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures udfclient 0.8.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/udfclient]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of udfclient 0.8.1:";;
+ esac
+ cat <<\_ACEOF
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <reinoud@NetBSD.org>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+udfclient configure 0.8.1
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## --------------------------------- ##
+## Report this to reinoud@NetBSD.org ##
+## --------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+$as_echo_n "checking for $2.$3... " >&6; }
+if eval \${$4+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$4
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by udfclient $as_me 0.8.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+#AC_CONFIG_SRCDIR([udf.c])
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+#AC_CONFIG_HEADER([config.h])
+
+
+#
+# Checks for programs.
+#
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+
+#
+# Check for OS dependent flags
+#
+case $host_os in
+linux*)
+ CPPFLAGS="$CPPFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500 -D__USE_BSD -D_FILE_OFFSET_BITS=64"
+ ;;
+darwin*)
+ # CPPFLAGS="-D_POSIX_C_SOURCE"
+ COPTS="$COPTS -fnested-functions"
+esac
+
+#
+# Checks for libraries.
+#
+# Rudimentary check for pthread library: if libpthread is found, then
+# link it in, otherwise pray it'll be in libc like FreeBSD's.
+#
+THREADLIB="$with_thread_libs"
+if test "$THREADLIB" = ""; then
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+if ${ac_cv_lib_pthread_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pthread_pthread_create=yes
+else
+ ac_cv_lib_pthread_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+ THREADLIB="-lpthread"
+fi
+
+fi
+if test "$THREADLIB" = ""; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
+$as_echo_n "checking for pthread_create in -lc_r... " >&6; }
+if ${ac_cv_lib_c_r_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc_r $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_c_r_pthread_create=yes
+else
+ ac_cv_lib_c_r_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_r_pthread_create" >&5
+$as_echo "$ac_cv_lib_c_r_pthread_create" >&6; }
+if test "x$ac_cv_lib_c_r_pthread_create" = xyes; then :
+ THREADLIB="-lc_r"
+fi
+
+fi
+if test "$THREADLIB" = ""; then
+ THREADLIB=
+fi
+
+CPPFLAGS="$CPPFLAGS $with_pthread_cflags"
+
+
+#
+# Check where to find clock_gettime. In Linux it is found in librt where
+# in BSD its in libc.
+#
+TIMELIB="$with_time_lib"
+if test "$TIMELIB" = ""; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lc" >&5
+$as_echo_n "checking for clock_gettime in -lc... " >&6; }
+if ${ac_cv_lib_c_clock_gettime+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_c_clock_gettime=yes
+else
+ ac_cv_lib_c_clock_gettime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_clock_gettime" >&5
+$as_echo "$ac_cv_lib_c_clock_gettime" >&6; }
+if test "x$ac_cv_lib_c_clock_gettime" = xyes; then :
+ TIMELIB="-lc"
+fi
+
+fi
+if test "$TIMELIB" = ""; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if ${ac_cv_lib_rt_clock_gettime+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_rt_clock_gettime=yes
+else
+ ac_cv_lib_rt_clock_gettime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then :
+ TIMELIB="-lrt"
+fi
+
+fi
+if test "$TIMELIB" = ""; then
+ TIMELIB=
+fi
+
+CPPFLAGS="$CPPFLAGS $with_time_cflags"
+
+
+#
+# Checks for various header files.
+#
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in endian.h sys/endian.h machine/endian.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in machine/bswap.h sys/bswap.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in machine/int_fmtio.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "machine/int_fmtio.h" "ac_cv_header_machine_int_fmtio_h" "$ac_includes_default"
+if test "x$ac_cv_header_machine_int_fmtio_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_MACHINE_INT_FMTIO_H 1
+_ACEOF
+ :
+else
+
+ CPPFLAGS="$CPPFLAGS -DNO_INT_FMTIO"
+
+fi
+
+done
+
+
+
+#
+# Check if we have the strlcpy function allready or have to emulate it
+#
+ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
+if test "x$ac_cv_func_strlcpy" = xyes; then :
+ :
+else
+
+ CPPFLAGS="$CPPFLAGS -DNO_STRLCPY"
+
+fi
+
+
+
+#
+# Check struct stat
+#
+stat_cppflags=""
+ac_fn_c_check_member "$LINENO" "struct stat" "st_atimespec" "ac_cv_member_struct_stat_st_atimespec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_atimespec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIMESPEC 1
+_ACEOF
+
+:
+else
+
+ # Non Posix struct stat, assume Linux
+ ac_fn_c_check_member "$LINENO" "struct stat" "st_atim" "ac_cv_member_struct_stat_st_atim" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_atim" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_ATIM 1
+_ACEOF
+
+
+ stat_cppflags="-Dst_atimespec=st_atim -Dst_ctimespec=st_ctim -Dst_mtimespec=st_mtim"
+
+else
+
+ echo "Failed to determine struct stat's timespec names"
+ exit
+
+fi
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtimespec" "ac_cv_member_struct_stat_st_birthtimespec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_birthtimespec" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC 1
+_ACEOF
+
+:
+else
+
+ stat_cppflags="$stat_cppflags -DNO_STAT_BIRTHTIME"
+
+fi
+
+CPPFLAGS="$CPPFLAGS $stat_cppflags"
+
+
+#
+# Check struct dirent
+#
+dirent_cppflags=""
+ac_fn_c_check_member "$LINENO" "struct dirent" "d_namlen" "ac_cv_member_struct_dirent_d_namlen" "$ac_includes_default"
+if test "x$ac_cv_member_struct_dirent_d_namlen" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_DIRENT_D_NAMLEN 1
+_ACEOF
+
+:
+else
+
+ dirent_cppflags="$dirent_cppflags -DNO_DIRENT_NAMLEN"
+
+fi
+
+CPPFLAGS="$CPPFLAGS $dirent_cppflags"
+
+
+#
+# Check for SCSI implementations
+#
+
+HAVE_SCSI="yes"
+SCSI_CFLAGS="$with_scsi_cflags"
+SCSI_LIB="$with_uscsi_lib"
+if test "$SCSI_CFLAGS" = ""; then
+ HAVE_SCSI=no
+fi
+
+# NetBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ for ac_header in dev/scsipi/scsipi_all.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "dev/scsipi/scsipi_all.h" "ac_cv_header_dev_scsipi_scsipi_all_h" "$ac_includes_default"
+if test "x$ac_cv_header_dev_scsipi_scsipi_all_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_DEV_SCSIPI_SCSIPI_ALL_H 1
+_ACEOF
+
+ SCSI_CFLAGS="-DUSCSI_SCSIPI"
+ HAVE_SCSI=yes
+
+fi
+
+done
+
+fi
+
+# Linux SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ for ac_header in scsi/sg.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "scsi/sg.h" "ac_cv_header_scsi_sg_h" "$ac_includes_default"
+if test "x$ac_cv_header_scsi_sg_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SCSI_SG_H 1
+_ACEOF
+
+ SCSI_CFLAGS="-DUSCSI_LINUX_SCSI"
+ HAVE_SCSI=yes
+
+fi
+
+done
+
+fi
+
+# FreeBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ for ac_header in camlib.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "camlib.h" "ac_cv_header_camlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_camlib_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_CAMLIB_H 1
+_ACEOF
+
+ SCSI_CFLAGS="-DUSCSI_FREEBSD_CAM"
+ HAVE_SCSI=yes
+ SCSI_LIB="-lcam"
+
+fi
+
+done
+
+fi
+
+# OpenBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ for ac_header in scsi/scsi_all.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "scsi/scsi_all.h" "ac_cv_header_scsi_scsi_all_h" "$ac_includes_default"
+if test "x$ac_cv_header_scsi_scsi_all_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SCSI_SCSI_ALL_H 1
+_ACEOF
+
+ CPPFLAGS="$CPPFLAGS -DUSCSI_SCSIPI"
+ HAVE_SCSI=yes
+
+fi
+
+done
+
+fi
+
+
+# process SCSI stack presence results
+if test "$HAVE_SCSI" = "yes"; then
+ CPPFLAGS="$CPPFLAGS -DSCSI $SCSI_CFLAGS"
+ BUILD_APPS="\$(APPS) \$(SCSI_APPS)"
+else
+ SCSI_LIB=
+ BUILD_APPS="\$(APPS)"
+fi
+
+
+
+
+
+#
+# Generate output files
+#
+ac_config_files="$ac_config_files Makefile"
+
+# AC_CONFIG_FILES([config.h])
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+ g
+ s/^\n//
+ s/\n/ /g
+ p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by udfclient $as_me 0.8.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <reinoud@NetBSD.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+udfclient config.status 0.8.1
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h | --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X " :F $CONFIG_FILES "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+ esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+echo ""
+echo "Compile the project with Posix compliant make; possibly installed as bmake or pmake"
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f375ee9
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,183 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.57)
+AC_INIT([udfclient], [0.8.1], [reinoud@NetBSD.org])
+#AC_CONFIG_SRCDIR([udf.c])
+AC_CANONICAL_HOST
+#AC_CONFIG_HEADER([config.h])
+
+
+#
+# Checks for programs.
+#
+AC_PROG_CC
+AC_PROG_INSTALL
+
+
+#
+# Check for OS dependent flags
+#
+case $host_os in
+linux*)
+ CPPFLAGS="$CPPFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500 -D__USE_BSD -D_FILE_OFFSET_BITS=64"
+ ;;
+darwin*)
+ # CPPFLAGS="-D_POSIX_C_SOURCE"
+ COPTS="$COPTS -fnested-functions"
+esac
+
+#
+# Checks for libraries.
+#
+# Rudimentary check for pthread library: if libpthread is found, then
+# link it in, otherwise pray it'll be in libc like FreeBSD's.
+#
+THREADLIB="$with_thread_libs"
+if test "$THREADLIB" = ""; then
+ AC_CHECK_LIB(pthread, pthread_create, [THREADLIB="-lpthread"])
+fi
+if test "$THREADLIB" = ""; then
+ AC_CHECK_LIB(c_r, pthread_create, [THREADLIB="-lc_r"])
+fi
+if test "$THREADLIB" = ""; then
+ THREADLIB=
+fi
+AC_SUBST(THREADLIB)
+CPPFLAGS="$CPPFLAGS $with_pthread_cflags"
+
+
+#
+# Check where to find clock_gettime. In Linux it is found in librt where
+# in BSD its in libc.
+#
+TIMELIB="$with_time_lib"
+if test "$TIMELIB" = ""; then
+ AC_CHECK_LIB(c, clock_gettime, [TIMELIB="-lc"])
+fi
+if test "$TIMELIB" = ""; then
+ AC_CHECK_LIB(rt, clock_gettime, [TIMELIB="-lrt"])
+fi
+if test "$TIMELIB" = ""; then
+ TIMELIB=
+fi
+AC_SUBST(TIMELIB)
+CPPFLAGS="$CPPFLAGS $with_time_cflags"
+
+
+#
+# Checks for various header files.
+#
+AC_HEADER_STDC
+AC_CHECK_HEADERS([endian.h sys/endian.h machine/endian.h])
+AC_CHECK_HEADERS([machine/bswap.h sys/bswap.h])
+AC_CHECK_HEADERS([machine/int_fmtio.h], [:], [
+ CPPFLAGS="$CPPFLAGS -DNO_INT_FMTIO"
+])
+
+
+#
+# Check if we have the strlcpy function allready or have to emulate it
+#
+AC_CHECK_FUNC([strlcpy], [:], [
+ CPPFLAGS="$CPPFLAGS -DNO_STRLCPY"
+])
+
+
+#
+# Check struct stat
+#
+stat_cppflags=""
+AC_CHECK_MEMBERS([struct stat.st_atimespec], [:], [
+ # Non Posix struct stat, assume Linux
+ AC_CHECK_MEMBERS([struct stat.st_atim], [
+ stat_cppflags="-Dst_atimespec=st_atim -Dst_ctimespec=st_ctim -Dst_mtimespec=st_mtim"
+ ], [
+ echo "Failed to determine struct stat's timespec names"
+ exit
+ ])
+])
+AC_CHECK_MEMBERS([struct stat.st_birthtimespec], [:], [
+ stat_cppflags="$stat_cppflags -DNO_STAT_BIRTHTIME"
+])
+CPPFLAGS="$CPPFLAGS $stat_cppflags"
+
+
+#
+# Check struct dirent
+#
+dirent_cppflags=""
+AC_CHECK_MEMBERS([struct dirent.d_namlen], [:], [
+ dirent_cppflags="$dirent_cppflags -DNO_DIRENT_NAMLEN"
+])
+CPPFLAGS="$CPPFLAGS $dirent_cppflags"
+
+
+#
+# Check for SCSI implementations
+#
+
+HAVE_SCSI="yes"
+SCSI_CFLAGS="$with_scsi_cflags"
+SCSI_LIB="$with_uscsi_lib"
+if test "$SCSI_CFLAGS" = ""; then
+ HAVE_SCSI=no
+fi
+
+# NetBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ AC_CHECK_HEADERS([dev/scsipi/scsipi_all.h], [
+ SCSI_CFLAGS="-DUSCSI_SCSIPI"
+ HAVE_SCSI=yes
+ ])
+fi
+
+# Linux SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ AC_CHECK_HEADERS([scsi/sg.h], [
+ SCSI_CFLAGS="-DUSCSI_LINUX_SCSI"
+ HAVE_SCSI=yes
+ ])
+fi
+
+# FreeBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ AC_CHECK_HEADERS([camlib.h], [
+ SCSI_CFLAGS="-DUSCSI_FREEBSD_CAM"
+ HAVE_SCSI=yes
+ SCSI_LIB="-lcam"
+ ])
+fi
+
+# OpenBSD SCSI stack
+if test "$HAVE_SCSI" = "no"; then
+ AC_CHECK_HEADERS([scsi/scsi_all.h], [
+ CPPFLAGS="$CPPFLAGS -DUSCSI_SCSIPI"
+ HAVE_SCSI=yes
+ ])
+fi
+
+
+# process SCSI stack presence results
+if test "$HAVE_SCSI" = "yes"; then
+ CPPFLAGS="$CPPFLAGS -DSCSI $SCSI_CFLAGS"
+ BUILD_APPS="\$(APPS) \$(SCSI_APPS)"
+else
+ SCSI_LIB=
+ BUILD_APPS="\$(APPS)"
+fi
+AC_SUBST(BUILD_APPS)
+AC_SUBST(SCSI_LIB)
+AC_SUBST(CPPFLAGS)
+
+
+#
+# Generate output files
+#
+AC_CONFIG_FILES([Makefile])
+# AC_CONFIG_FILES([config.h])
+AC_OUTPUT
+
+echo ""
+echo "Compile the project with Posix compliant make; possibly installed as bmake or pmake"
+
diff --git a/defs.h b/defs.h
new file mode 100644
index 0000000..ce773ea
--- /dev/null
+++ b/defs.h
@@ -0,0 +1,101 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf.h" is part of the UDFclient toolkit.
+ * File $Id: defs.h,v 1.7 2015/01/02 21:23:18 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef _DEFS_H_
+#define _DEFS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <inttypes.h>
+
+
+#ifdef NO_INT_FMTIO
+ /* assume 32 bits :-/ */
+#ifndef PRIu32
+# define PRIu32 "u"
+# define PRIx32 "x"
+# define PRIu64 "lld"
+# define PRIx64 "llx"
+#endif
+#endif
+
+
+/* exported flags */
+extern int udf_verbose;
+
+#define UDF_MNT_RDONLY 1
+#define UDF_MNT_FORCE 2
+
+#define UDF_VERBLEV_NONE 0
+#define UDF_VERBLEV_ACTIONS 1
+#define UDF_VERBLEV_TABLES 2
+#define UDF_VERBLEV_MAX 3
+
+/* constants to identify what kind of identifier we are dealing with */
+#define UDF_REGID_DOMAIN 1
+#define UDF_REGID_UDF 2
+#define UDF_REGID_IMPLEMENTATION 3
+#define UDF_REGID_APPLICATION 4
+#define UDF_REGID_NAME 99 /* 99? */
+
+/* DON'T change these: they identify 13thmonkey's UDF toolkit */
+#define APP_NAME "*UDFtoolkit"
+#define APP_VERSION_MAIN 0
+#define APP_VERSION_SUB 7
+#define IMPL_NAME "*13thMonkey UDFtoolkit"
+
+/* RW content hint for allocation and other purposes */
+#define UDF_C_DSCR 0
+#define UDF_C_USERDATA 1
+#define UDF_C_FIDS 2
+#define UDF_C_NODE 3
+
+
+/* end of Configuration */
+
+
+#if 1
+# define UDF_VERBOSE(op) if (udf_verbose) { op; }
+# define UDF_VERBOSE_LEVEL(level, op) if (udf_verbose >= (level)) { op; }
+# define UDF_VERBOSE_TABLES(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_TABLES, op)
+# define UDF_VERBOSE_MAX(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_MAX, op)
+#else
+# define UDF_VERBOSE(op)
+# define UDF_VERBOSE_LEVEL(level, op)
+# define UDF_VERBOSE_TABLES(op)
+# define UDF_VERBOSE_MAX(op)
+#endif
+
+#endif /* _DEFS_H_ */
+
diff --git a/dirhash.h b/dirhash.h
new file mode 100644
index 0000000..d0706d0
--- /dev/null
+++ b/dirhash.h
@@ -0,0 +1,95 @@
+/* $NetBSD: dirhash.h,v 1.4 2008/10/30 23:02:12 rmind Exp $ */
+
+/*
+ * Copyright (c) 2008, 2011 Reinoud Zandijk
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_DIRHASH_H_
+#define _SYS_DIRHASH_H_
+
+#include <dirent.h>
+#include "queue.h"
+
+#ifndef DIRHASH_SIZE
+#define DIRHASH_SIZE (1024*1024)
+#endif
+
+#define DIRHASH_HASHBITS 5
+#define DIRHASH_HASHSIZE (1 << DIRHASH_HASHBITS)
+#define DIRHASH_HASHMASK (DIRHASH_HASHSIZE - 1)
+
+#ifdef NO_DIRENT_NAMLEN
+# define DIRENT_NAMLEN(d) strlen((d)->d_name)
+#else
+# define DIRENT_NAMLEN(d) (d)->d_namlen
+#endif
+
+
+#ifndef _DIRENT_SIZE
+ /* guess */
+# define _DIRENT_SIZE(d) (32 + DIRENT_NAMLEN(d))
+#endif
+
+
+/* dirent's d_namlen is to avoid useless costly fid->dirent translations */
+struct dirhash_entry {
+ uint32_t hashvalue;
+ uint64_t offset;
+ uint32_t d_namlen;
+ uint32_t entry_size;
+ LIST_ENTRY(dirhash_entry) next;
+};
+
+struct dirhash {
+ uint32_t flags;
+ uint32_t size; /* in bytes */
+ uint32_t refcnt;
+ LIST_HEAD(, dirhash_entry) entries[DIRHASH_HASHSIZE];
+ LIST_HEAD(, dirhash_entry) free_entries;
+ TAILQ_ENTRY(dirhash) next;
+};
+
+#define DIRH_PURGED 0x0001 /* dirhash has been purged */
+#define DIRH_COMPLETE 0x0002 /* dirhash is complete */
+#define DIRH_BROKEN 0x0004 /* dirhash is broken on readin */
+#define DIRH_FLAGBITS "\10\1DIRH_PURGED\2DIRH_COMPLETE\3DIRH_BROKEN"
+
+void dirhash_init(void);
+/* void dirhash_finish(void); */
+
+void dirhash_purge(struct dirhash **);
+void dirhash_purge_entries(struct dirhash *);
+void dirhash_get(struct dirhash **);
+void dirhash_put(struct dirhash *);
+void dirhash_enter(struct dirhash *, struct dirent *, uint64_t,
+ uint32_t, int);
+void dirhash_enter_freed(struct dirhash *, uint64_t, uint32_t);
+void dirhash_mark_freed(struct dirhash *, struct dirhash_entry *,
+ struct dirent *);
+int dirhash_lookup(struct dirhash *, const char *, int,
+ struct dirhash_entry **);
+int dirhash_lookup_freed(struct dirhash *, uint32_t,
+ struct dirhash_entry **);
+
+#endif /* _SYS_DIRHASH_H_ */
diff --git a/ecma167-udf.h b/ecma167-udf.h
new file mode 100644
index 0000000..73c72b7
--- /dev/null
+++ b/ecma167-udf.h
@@ -0,0 +1,844 @@
+/* $NetBSD: ecma167-udf.h,v 1.5 2006/10/22 00:06:48 reinoud Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * Extended and adapted for UDFv2.50+ bij Reinoud Zandijk based on the
+ * origional by Scott Long.
+ *
+ * 20030508 Made some small typo and explainatory comments
+ * 20030510 Added UDF 2.01 structures
+ * 20030519 Added/correct comments on multi-partitioned logical volume space
+ * 20050616 Added pseudo overwrite
+ * 20050624 Added the missing extended attribute types and `magic values'.
+ * 20051106 Reworked some implementation use parts
+ *
+ */
+
+
+#ifndef _FS_UDF_ECMA167_UDF_H_
+#define _FS_UDF_ECMA167_UDF_H_
+
+
+/*
+ * in case of an older gcc versions, define the __packed as explicit
+ * attribute
+ */
+
+/*
+ * You may specify the `aligned' and `transparent_union' attributes either in
+ * a `typedef' declaration or just past the closing curly brace of a complete
+ * enum, struct or union type _definition_ and the `packed' attribute only
+ * past the closing brace of a definition. You may also specify attributes
+ * between the enum, struct or union tag and the name of the type rather than
+ * after the closing brace.
+*/
+
+#if 1
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+#endif
+
+
+/* ecma167-udf.h */
+
+/* Volume recognition sequence ECMA 167 rev. 3 16.1 */
+struct vrs_desc {
+ uint8_t struct_type;
+ uint8_t identifier[5];
+ uint8_t version;
+ uint8_t data[2041];
+} __packed;
+
+
+#define VRS_NSR02 "NSR02"
+#define VRS_NSR03 "NSR03"
+#define VRS_BEA01 "BEA01"
+#define VRS_TEA01 "TEA01"
+#define VRS_CD001 "CD001"
+#define VRS_CDW02 "CDW02"
+
+
+/* Structure/definitions/constants a la ECMA 167 rev. 3 */
+
+
+#define MAX_TAGID_VOLUMES 9
+/* Tag identifiers */
+enum {
+ TAGID_SPARING_TABLE = 0,
+ TAGID_PRI_VOL = 1,
+ TAGID_ANCHOR = 2,
+ TAGID_VOL = 3,
+ TAGID_IMP_VOL = 4,
+ TAGID_PARTITION = 5,
+ TAGID_LOGVOL = 6,
+ TAGID_UNALLOC_SPACE = 7,
+ TAGID_TERM = 8,
+ TAGID_LOGVOL_INTEGRITY= 9,
+ TAGID_FSD = 256,
+ TAGID_FID = 257,
+ TAGID_ALLOCEXTENT = 258,
+ TAGID_INDIRECT_ENTRY = 259,
+ TAGID_ICB_TERM = 260,
+ TAGID_FENTRY = 261,
+ TAGID_EXTATTR_HDR = 262,
+ TAGID_UNALL_SP_ENTRY = 263,
+ TAGID_SPACE_BITMAP = 264,
+ TAGID_PART_INTEGRETY = 265,
+ TAGID_EXTFENTRY = 266,
+ TAGID_MAX = 266
+};
+
+
+enum {
+ UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT = 1,
+ UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT = 2
+};
+
+
+enum {
+ UDF_ACCESSTYPE_NOT_SPECIFIED = 0, /* unknown */
+ UDF_ACCESSTYPE_PSEUDO_OVERWITE = 0, /* Pseudo overwritable, f.e. BD-R's LOW */
+ UDF_ACCESSTYPE_READ_ONLY = 1, /* really only readable */
+ UDF_ACCESSTYPE_WRITE_ONCE = 2, /* write once and you're done */
+ UDF_ACCESSTYPE_REWRITEABLE = 3, /* may need extra work to rewrite */
+ UDF_ACCESSTYPE_OVERWRITABLE = 4 /* no limits on rewriting; harddisc f.e.*/
+};
+
+
+/* Descriptor tag [3/7.2] */
+struct desc_tag {
+ uint16_t id;
+ uint16_t descriptor_ver;
+ uint8_t cksum;
+ uint8_t reserved;
+ uint16_t serial_num;
+ uint16_t desc_crc;
+ uint16_t desc_crc_len;
+ uint32_t tag_loc;
+} __packed;
+#define UDF_DESC_TAG_LENGTH 16
+
+
+/* Recorded Address [4/7.1] */
+struct lb_addr { /* within partition space */
+ uint32_t lb_num;
+ uint16_t part_num;
+} __packed;
+
+
+/* Extent Descriptor [3/7.1] */
+struct extent_ad {
+ uint32_t len;
+ uint32_t loc;
+} __packed;
+
+
+/* Short Allocation Descriptor [4/14.14.1] */
+struct short_ad {
+ uint32_t len;
+ uint32_t lb_num;
+} __packed;
+
+
+/* Long Allocation Descriptor [4/14.14.2] */
+struct UDF_ADImp_use {
+ uint16_t flags;
+ uint32_t unique_id;
+} __packed;
+#define UDF_ADIMP_FLAGS_EXTENT_ERASED 1
+
+
+struct long_ad {
+ uint32_t len;
+ struct lb_addr loc; /* within a logical volume mapped partition space !! */
+ union {
+ uint8_t bytes[6];
+ struct UDF_ADImp_use im_used;
+ } impl;
+} __packed;
+#define longad_uniqueid impl.im_used.unique_id
+
+
+/* Extended Allocation Descriptor [4/14.14.3] ; identifies an extent of allocation descriptors ; also in UDF ? */
+struct ext_ad {
+ uint32_t ex_len;
+ uint32_t rec_len;
+ uint32_t inf_len;
+ struct lb_addr ex_loc;
+ uint8_t reserved[2];
+} __packed;
+
+
+/* ICB : Information Control Block; positioning */
+union icb {
+ struct short_ad s_ad;
+ struct long_ad l_ad;
+ struct ext_ad e_ad;
+};
+
+
+/* short/long/ext extent have flags encoded in length */
+#define UDF_EXT_ALLOCATED (0<<30)
+#define UDF_EXT_FREED (1<<30)
+#define UDF_EXT_ALLOCATED_BUT_NOT_USED (1<<30)
+#define UDF_EXT_FREE (2<<30)
+#define UDF_EXT_REDIRECT (3<<30)
+#define UDF_EXT_FLAGS(len) ((len) & (3<<30))
+#define UDF_EXT_LEN(len) ((len) & ((1<<30)-1))
+
+
+/* Character set spec [1/7.2.1] */
+struct charspec {
+ uint8_t type;
+ uint8_t inf[63];
+} __packed;
+
+
+struct pathcomp {
+ uint8_t type;
+ uint8_t l_ci;
+ uint16_t comp_filever;
+ uint8_t ident[256];
+} __packed;
+#define UDF_PATH_COMP_SIZE 4
+#define UDF_PATH_COMP_RESERVED 0
+#define UDF_PATH_COMP_ROOT 1
+#define UDF_PATH_COMP_MOUNTROOT 2
+#define UDF_PATH_COMP_PARENTDIR 3
+#define UDF_PATH_COMP_CURDIR 4
+#define UDF_PATH_COMP_NAME 5
+
+
+/* Timestamp [1/7.3] */
+struct timestamp {
+ uint16_t type_tz;
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t centisec;
+ uint8_t hund_usec;
+ uint8_t usec;
+} __packed;
+#define UDF_TIMESTAMP_SIZE 12
+
+
+/* Entity Identifier [1/7.4] */
+#define UDF_REGID_ID_SIZE 23
+struct regid {
+ uint8_t flags;
+ uint8_t id[UDF_REGID_ID_SIZE];
+ uint8_t id_suffix[8];
+} __packed;
+
+
+/* ICB Tag [4/14.6] */
+struct icb_tag {
+ uint32_t prev_num_dirs;
+ uint16_t strat_type;
+ uint8_t strat_param[2];
+ uint16_t max_num_entries;
+ uint8_t reserved;
+ uint8_t file_type;
+ struct lb_addr parent_icb;
+ uint16_t flags;
+} __packed;
+#define UDF_ICB_TAG_FLAGS_ALLOC_MASK 0x03
+#define UDF_ICB_SHORT_ALLOC 0x00
+#define UDF_ICB_LONG_ALLOC 0x01
+#define UDF_ICB_EXT_ALLOC 0x02
+#define UDF_ICB_INTERN_ALLOC 0x03
+
+#define UDF_ICB_TAG_FLAGS_DIRORDERED (1<< 3)
+#define UDF_ICB_TAG_FLAGS_NONRELOC (1<< 4)
+#define UDF_ICB_TAG_FLAGS_CONTIGUES (1<< 9)
+#define UDF_ICB_TAG_FLAGS_MULTIPLEVERS (1<<12)
+
+#define UDF_ICB_TAG_FLAGS_SETUID (1<< 6)
+#define UDF_ICB_TAG_FLAGS_SETGID (1<< 7)
+#define UDF_ICB_TAG_FLAGS_STICKY (1<< 8)
+
+#define UDF_ICB_FILETYPE_UNKNOWN 0
+#define UDF_ICB_FILETYPE_UNALLOCSPACE 1
+#define UDF_ICB_FILETYPE_PARTINTEGRITY 2
+#define UDF_ICB_FILETYPE_INDIRECTENTRY 3
+#define UDF_ICB_FILETYPE_DIRECTORY 4
+#define UDF_ICB_FILETYPE_RANDOMACCESS 5
+#define UDF_ICB_FILETYPE_BLOCKDEVICE 6
+#define UDF_ICB_FILETYPE_CHARDEVICE 7
+#define UDF_ICB_FILETYPE_EXTATTRREC 8
+#define UDF_ICB_FILETYPE_FIFO 9
+#define UDF_ICB_FILETYPE_SOCKET 10
+#define UDF_ICB_FILETYPE_TERM 11
+#define UDF_ICB_FILETYPE_SYMLINK 12
+#define UDF_ICB_FILETYPE_STREAMDIR 13
+#define UDF_ICB_FILETYPE_VAT 248
+#define UDF_ICB_FILETYPE_REALTIME 249
+#define UDF_ICB_FILETYPE_META_MAIN 250
+#define UDF_ICB_FILETYPE_META_MIRROR 251
+#define UDF_ICB_FILETYPE_META_BITMAP 252
+
+
+/* Anchor Volume Descriptor Pointer [3/10.2] */
+struct anchor_vdp {
+ struct desc_tag tag;
+ struct extent_ad main_vds_ex; /* to main volume descriptor set ; 16 sectors min */
+ struct extent_ad reserve_vds_ex; /* copy of main volume descriptor set ; 16 sectors min */
+} __packed;
+
+
+/* Volume Descriptor Pointer [3/10.3] */
+struct vol_desc_ptr {
+ struct desc_tag tag; /* use for extending the volume descriptor space */
+ uint32_t vds_number;
+ struct extent_ad next_vds_ex; /* points to the next block for volume descriptor space */
+} __packed;
+
+
+/* Primary Volume Descriptor [3/10.1] */
+struct pri_vol_desc {
+ struct desc_tag tag;
+ uint32_t seq_num; /* MAX prevail */
+ uint32_t pvd_num; /* assigned by author; 0 is special as in it may only occure once */
+ char vol_id[32]; /* KEY ; main identifier of this disc */
+ uint16_t vds_num; /* volume descriptor number; i.e. what volume number is it */
+ uint16_t max_vol_seq; /* maximum volume descriptor number known */
+ uint16_t ichg_lvl;
+ uint16_t max_ichg_lvl;
+ uint32_t charset_list;
+ uint32_t max_charset_list;
+ char volset_id[128]; /* KEY ; if part of a multi-disc set or a band of volumes */
+ struct charspec desc_charset; /* KEY according to ECMA 167 */
+ struct charspec explanatory_charset;
+ struct extent_ad vol_abstract;
+ struct extent_ad vol_copyright;
+ struct regid app_id;
+ struct timestamp time;
+ struct regid imp_id;
+ uint8_t imp_use[64];
+ uint32_t prev_vds_loc; /* location of predecessor _lov ? */
+ uint16_t flags; /* bit 0 : if set indicates volume set name is meaningfull */
+ uint8_t reserved[22];
+} __packed;
+
+
+/* UDF specific implementation use part of the implementation use volume descriptor */
+struct udf_lv_info {
+ struct charspec lvi_charset;
+ char logvol_id[128];
+
+ char lvinfo1[36];
+ char lvinfo2[36];
+ char lvinfo3[36];
+
+ struct regid impl_id;
+ uint8_t impl_use[128];
+} __packed;
+
+
+/* Implementation use Volume Descriptor */
+struct impvol_desc {
+ struct desc_tag tag;
+ uint32_t seq_num;
+ struct regid impl_id;
+ union {
+ struct udf_lv_info lv_info;
+ char impl_use[460];
+ }_impl_use;
+} __packed;
+
+
+/* Logical Volume Descriptor [3/10.6] */
+struct logvol_desc {
+ struct desc_tag tag;
+ uint32_t seq_num; /* MAX prevail */
+ struct charspec desc_charset; /* KEY */
+ char logvol_id[128]; /* KEY */
+ uint32_t lb_size;
+ struct regid domain_id;
+ union {
+ struct long_ad fsd_loc; /* to fileset descriptor SEQUENCE */
+ uint8_t logvol_content_use[16];
+ } _lvd_use;
+ uint32_t mt_l; /* Partition map length */
+ uint32_t n_pm; /* Number of partition maps */
+ struct regid imp_id;
+ uint8_t imp_use[128];
+ struct extent_ad integrity_seq_loc;
+ uint8_t maps[1];
+} __packed;
+#define lv_fsd_loc _lvd_use.fsd_loc
+
+#define UDF_INTEGRITY_OPEN 0
+#define UDF_INTEGRITY_CLOSED 1
+
+
+#define UDF_PMAP_SIZE 64
+
+/* Type 1 Partition Map [3/10.7.2] */
+struct part_map_1 {
+ uint8_t type;
+ uint8_t len;
+ uint16_t vol_seq_num;
+ uint16_t part_num;
+} __packed;
+
+
+/* Type 2 Partition Map [3/10.7.3] */
+struct part_map_2 {
+ uint8_t type;
+ uint8_t len;
+ uint8_t reserved[2];
+ struct regid part_id;
+ uint16_t vol_seq_num;
+ uint16_t part_num;
+ uint8_t reserved2[24];
+} __packed;
+
+
+/* Virtual Partition Map [UDF 2.01/2.2.8] */
+struct part_map_virt {
+ uint8_t type;
+ uint8_t len;
+ uint8_t reserved[2];
+ struct regid id;
+ uint16_t vol_seq_num;
+ uint16_t part_num;
+ uint8_t reserved1[24];
+} __packed;
+
+
+/* Sparable Partition Map [UDF 2.01/2.2.9] */
+struct part_map_spare {
+ uint8_t type;
+ uint8_t len;
+ uint8_t reserved[2];
+ struct regid id;
+ uint16_t vol_seq_num;
+ uint16_t part_num;
+ uint16_t packet_len;
+ uint8_t n_st; /* Number of redundant sparing tables range 1-4 */
+ uint8_t reserved1;
+ uint32_t st_size; /* size of EACH sparing table */
+ uint32_t st_loc[1]; /* locations of sparing tables */
+} __packed;
+
+
+/* Metadata Partition Map [UDF 2.50/2.2.10] */
+struct part_map_meta {
+ uint8_t type;
+ uint8_t len;
+ uint8_t reserved[2];
+ struct regid id;
+ uint16_t vol_seq_num;
+ uint16_t part_num;
+ uint32_t meta_file_lbn; /* logical block number for file entry within part_num */
+ uint32_t meta_mirror_file_lbn;
+ uint32_t meta_bitmap_file_lbn;
+ uint32_t alloc_unit_size; /* allocation unit size in blocks */
+ uint16_t alignment_unit_size; /* alignment nessisary in blocks */
+ uint8_t flags;
+ uint8_t reserved1[5];
+} __packed;
+#define METADATA_DUPLICATED 1
+
+
+union udf_pmap {
+ uint8_t data[UDF_PMAP_SIZE];
+ struct part_map_1 pm1;
+ struct part_map_2 pm2;
+ struct part_map_virt pmv;
+ struct part_map_spare pms;
+ struct part_map_meta pmm;
+};
+
+
+/* Sparing Map Entry [UDF 2.01/2.2.11] */
+struct spare_map_entry {
+ uint32_t org; /* partion relative address */
+ uint32_t map; /* absolute disc address (!) can be in partion, but doesn't have to be */
+} __packed;
+
+
+/* Sparing Table [UDF 2.01/2.2.11] */
+struct udf_sparing_table {
+ struct desc_tag tag;
+ struct regid id;
+ uint16_t rt_l; /* Relocation Table len */
+ uint8_t reserved[2];
+ uint32_t seq_num;
+ struct spare_map_entry entries[1];
+} __packed;
+
+
+#define UDF_NO_PREV_VAT 0xffffffff
+/* UDF 1.50 VAT suffix [UDF 2.2.10 (UDF 1.50 spec)] */
+struct udf_oldvat_tail {
+ struct regid id; /* "*UDF Virtual Alloc Tbl" */
+ uint32_t prev_vat;
+} __packed;
+
+
+/* VAT table [UDF 2.0.1/2.2.10] */
+struct udf_vat {
+ uint16_t header_len;
+ uint16_t impl_use_len;
+ char logvol_id[128]; /* newer version of the LVD one */
+ uint32_t prev_vat;
+ uint32_t num_files;
+ uint32_t num_directories;
+ uint16_t min_udf_readver;
+ uint16_t min_udf_writever;
+ uint16_t max_udf_writever;
+ uint16_t reserved;
+ uint8_t data[1]; /* impl.use followed by VAT entries (uint32_t) */
+} __packed;
+
+
+/* Space bitmap descriptor as found in the partition header descriptor */
+struct space_bitmap_desc {
+ struct desc_tag tag; /* TagId 264 */
+ uint32_t num_bits; /* number of bits */
+ uint32_t num_bytes; /* bytes that contain it */
+ uint8_t data[1];
+} __packed;
+
+
+/* Unalloc space entry as found in the partition header descriptor */
+struct space_entry_desc {
+ struct desc_tag tag; /* TagId 263 */
+ struct icb_tag icbtag; /* type 1 */
+ uint32_t l_ad; /* in bytes */
+ uint8_t entry[1];
+} __packed;
+
+
+/* Partition header descriptor; in the contents_use of part_desc */
+struct part_hdr_desc {
+ struct short_ad unalloc_space_table;
+ struct short_ad unalloc_space_bitmap;
+ struct short_ad part_integrity_table; /* has to be ZERO for UDF */
+ struct short_ad freed_space_table;
+ struct short_ad freed_space_bitmap;
+ uint8_t reserved[88];
+} __packed;
+
+
+/* Partition Descriptor [3/10.5] */
+struct part_desc {
+ struct desc_tag tag;
+ uint32_t seq_num; /* MAX prevailing */
+ uint16_t flags; /* bit 0 : if set the space is allocated */
+ uint16_t part_num; /* KEY */
+ struct regid contents;
+ union {
+ struct part_hdr_desc part_hdr;
+ uint8_t contents_use[128];
+ } _impl_use;
+ uint32_t access_type; /* R/W, WORM etc. */
+ uint32_t start_loc; /* start of partion with given length */
+ uint32_t part_len;
+ struct regid imp_id;
+ uint8_t imp_use[128];
+ uint8_t reserved[156];
+} __packed;
+#define pd_part_hdr _impl_use.part_hdr
+#define UDF_PART_FLAG_ALLOCATED 1
+
+
+/* Unallocated Space Descriptor (UDF 2.01/2.2.5) */
+struct unalloc_sp_desc {
+ struct desc_tag tag;
+ uint32_t seq_num; /* MAX prevailing */
+ uint32_t alloc_desc_num;
+ struct extent_ad alloc_desc[1];
+} __packed;
+
+
+/* Logical Volume Integrity Descriptor [3/30.10] */
+struct logvolhdr {
+ uint64_t next_unique_id;
+ /* rest reserved */
+} __packed;
+
+
+struct udf_logvol_info {
+ struct regid impl_id;
+ uint32_t num_files;
+ uint32_t num_directories;
+ uint16_t min_udf_readver;
+ uint16_t min_udf_writever;
+ uint16_t max_udf_writever;
+} __packed;
+
+
+struct logvol_int_desc {
+ struct desc_tag tag;
+ struct timestamp time;
+ uint32_t integrity_type;
+ struct extent_ad next_extent;
+ union {
+ struct logvolhdr logvolhdr;
+ int8_t reserved[32];
+ } _impl_use;
+ uint32_t num_part;
+ uint32_t l_iu;
+ uint32_t tables[1]; /* Freespace table, Sizetable, Implementation use */
+} __packed;
+#define lvint_next_unique_id _impl_use.logvolhdr.next_unique_id
+
+
+/* File Set Descriptor [4/14.1] */
+struct fileset_desc {
+ struct desc_tag tag;
+ struct timestamp time;
+ uint16_t ichg_lvl;
+ uint16_t max_ichg_lvl;
+ uint32_t charset_list;
+ uint32_t max_charset_list;
+ uint32_t fileset_num; /* key! */
+ uint32_t fileset_desc_num;
+ struct charspec logvol_id_charset;
+ char logvol_id[128]; /* for recovery */
+ struct charspec fileset_charset;
+ char fileset_id[32]; /* Mountpoint !! */
+ char copyright_file_id[32];
+ char abstract_file_id[32];
+ struct long_ad rootdir_icb; /* to rootdir; icb->virtual ? */
+ struct regid domain_id;
+ struct long_ad next_ex; /* to the next fileset_desc extent */
+ struct long_ad streamdir_icb; /* streamdir; needed? */
+ uint8_t reserved[32];
+} __packed;
+
+
+/* File Identifier Descriptor [4/14.4] */
+struct fileid_desc {
+ struct desc_tag tag;
+ uint16_t file_version_num;
+ uint8_t file_char;
+ uint8_t l_fi; /* Length of file identifier area */
+ struct long_ad icb;
+ uint16_t l_iu; /* Length of implementation use area */
+ uint8_t data[0];
+} __packed;
+#define UDF_FID_SIZE 38
+#define UDF_FILE_CHAR_VIS (1 << 0) /* Invisible */
+#define UDF_FILE_CHAR_DIR (1 << 1) /* Directory */
+#define UDF_FILE_CHAR_DEL (1 << 2) /* Deleted */
+#define UDF_FILE_CHAR_PAR (1 << 3) /* Parent Directory */
+#define UDF_FILE_CHAR_META (1 << 4) /* Stream metadata */
+
+
+/* Extended attributes [4/14.10.1] */
+struct extattrhdr_desc {
+ struct desc_tag tag;
+ uint32_t impl_attr_loc; /* offsets within this descriptor */
+ uint32_t appl_attr_loc; /* ditto */
+} __packed;
+#define UDF_IMPL_ATTR_LOC_NOT_PRESENT 0xffffffff
+#define UDF_APPL_ATTR_LOC_NOT_PRESENT 0xffffffff
+
+
+/* Extended attribute entry [4/48.10.2] */
+struct extattr_entry {
+ uint32_t type;
+ uint8_t subtype;
+ uint8_t reserved[3];
+ uint32_t a_l;
+} __packed;
+
+
+/* Extended attribute entry; type 2048 [4/48.10.8] */
+struct impl_extattr_entry {
+ struct extattr_entry hdr;
+ uint32_t iu_l;
+ struct regid imp_id;
+ uint8_t data[1];
+} __packed;
+
+
+/* Extended attribute entry; type 65 536 [4/48.10.9] */
+struct appl_extattr_entry {
+ struct extattr_entry hdr;
+ uint32_t au_l;
+ struct regid appl_id;
+ uint8_t data[1];
+} __packed;
+
+
+/* File Times attribute entry; type 5 or type 6 [4/48.10.5], [4/48.10.6] */
+struct filetimes_extattr_entry {
+ struct extattr_entry hdr;
+ uint32_t d_l; /* length of times[] data following */
+ uint32_t existence; /* bitmask */
+ struct timestamp times[1]; /* in order of assending bits */
+} __packed;
+#define UDF_FILETIMES_ATTR_NO 5
+#define UDF_FILETIMES_FILE_CREATION 1
+#define UDF_FILETIMES_FILE_DELETION 4
+#define UDF_FILETIMES_FILE_EFFECTIVE 8
+#define UDF_FILETIMES_FILE_BACKUPED 16
+#define UDF_FILETIMES_ATTR_SIZE(no) (20 + (no)*sizeof(struct timestamp))
+
+
+/* Device Specification Extended Attribute [4/4.10.7] */
+struct device_extattr_entry {
+ struct extattr_entry hdr;
+ uint32_t iu_l; /* length of implementation use */
+ uint32_t major;
+ uint32_t minor;
+ uint8_t data[1]; /* UDF: if nonzero length, contain developer ID regid */
+} __packed;
+#define UDF_DEVICESPEC_ATTR_NO 12
+
+
+/* VAT LV extension Extended Attribute [UDF 3.3.4.5.1.3] 1.50 errata */
+struct vatlvext_extattr_entry {
+ uint64_t unique_id_chk; /* needs to be copy of ICB's */
+ uint32_t num_files;
+ uint32_t num_directories;
+ char logvol_id[128]; /* replaces logvol name */
+} __packed;
+
+
+/* File Entry [4/14.9] */
+struct file_entry {
+ struct desc_tag tag;
+ struct icb_tag icbtag;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t perm;
+ uint16_t link_cnt;
+ uint8_t rec_format;
+ uint8_t rec_disp_attr;
+ uint32_t rec_len;
+ uint64_t inf_len;
+ uint64_t logblks_rec;
+ struct timestamp atime;
+ struct timestamp mtime;
+ struct timestamp attrtime;
+ uint32_t ckpoint;
+ struct long_ad ex_attr_icb;
+ struct regid imp_id;
+ uint64_t unique_id;
+ uint32_t l_ea; /* Length of extended attribute area */
+ uint32_t l_ad; /* Length of allocation descriptors */
+ uint8_t data[1];
+} __packed;
+#define UDF_FENTRY_SIZE 176
+#define UDF_FENTRY_PERM_USER_MASK 0x07
+#define UDF_FENTRY_PERM_GRP_MASK 0xE0
+#define UDF_FENTRY_PERM_OWNER_MASK 0x1C00
+
+
+/* Extended File Entry [4/48.17] */
+struct extfile_entry {
+ struct desc_tag tag;
+ struct icb_tag icbtag;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t perm;
+ uint16_t link_cnt;
+ uint8_t rec_format;
+ uint8_t rec_disp_attr;
+ uint32_t rec_len;
+ uint64_t inf_len;
+ uint64_t obj_size;
+ uint64_t logblks_rec;
+ struct timestamp atime;
+ struct timestamp mtime;
+ struct timestamp ctime;
+ struct timestamp attrtime;
+ uint32_t ckpoint;
+ uint32_t reserved1;
+ struct long_ad ex_attr_icb;
+ struct long_ad streamdir_icb;
+ struct regid imp_id;
+ uint64_t unique_id;
+ uint32_t l_ea; /* Length of extended attribute area */
+ uint32_t l_ad; /* Length of allocation descriptors */
+ uint8_t data[1];
+} __packed;
+#define UDF_EXTFENTRY_SIZE 216
+
+
+/* Indirect entry [ecma 48.7] */
+struct indirect_entry {
+ struct desc_tag tag;
+ struct icb_tag icbtag;
+ struct long_ad indirect_icb;
+} __packed;
+
+
+/* Allocation extent descriptor [ecma 48.5] */
+struct alloc_ext_entry {
+ struct desc_tag tag;
+ uint32_t prev_entry;
+ uint32_t l_ad;
+ uint8_t data[1];
+} __packed;
+
+
+union dscrptr {
+ struct desc_tag tag;
+ struct anchor_vdp avdp;
+ struct vol_desc_ptr vdp;
+ struct pri_vol_desc pvd;
+ struct logvol_desc lvd;
+ struct unalloc_sp_desc usd;
+ struct logvol_int_desc lvid;
+ struct impvol_desc ivd;
+ struct part_desc pd;
+ struct fileset_desc fsd;
+ struct fileid_desc fid;
+ struct file_entry fe;
+ struct extfile_entry efe;
+ struct extattrhdr_desc eahd;
+ struct indirect_entry inde;
+ struct alloc_ext_entry aee;
+ struct udf_sparing_table spt;
+ struct space_bitmap_desc sbd;
+ struct space_entry_desc sed;
+};
+
+
+/* Useful defines */
+
+#define GETICB(ad_type, fentry, offset) \
+ (struct ad_type *)&fentry->data[offset]
+
+#define GETICBLEN(ad_type, icb) ((struct ad_type *)(icb))->len
+
+
+#endif /* !_FS_UDF_ECMA167_UDF_H_ */
+
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..45041ab
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,103 @@
+/* $NetBSD: hash.h,v 1.6 2008/04/28 20:24:10 martin Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_HASH_H_
+#define _SYS_HASH_H_
+
+#include <sys/types.h>
+#ifdef __HAVE_MACHINE_HASH_H
+#include <machine/hash.h>
+#endif
+
+
+#ifndef __HAVE_HASH32_BUF /* not overridden by MD hash */
+
+#define HASH32_BUF_INIT 5381
+
+/*
+ * uint32_t
+ * hash32_buf(const void *bf, size_t len, uint32_t hash)
+ * return a 32 bit hash of the binary buffer buf (size len),
+ * seeded with an initial hash value of hash (usually HASH32_BUF_INIT).
+ */
+static __inline uint32_t
+hash32_buf(const void *bf, size_t len, uint32_t hash)
+{
+ const uint8_t *s = bf;
+
+ while (len-- != 0) /* "nemesi": k=257, r=r*257 */
+ hash = hash * 257 + *s++;
+ return (hash * 257);
+}
+#endif /* __HAVE_HASH32_BUF */
+
+
+#ifndef __HAVE_HASH32_STR /* not overridden by MD hash */
+
+#define HASH32_STR_INIT 5381
+/*
+ * uint32_t
+ * hash32_str(const void *bf, uint32_t hash)
+ * return a 32 bit hash of NUL terminated ASCII string buf,
+ * seeded with an initial hash value of hash (usually HASH32_STR_INIT).
+ */
+static __inline uint32_t
+hash32_str(const void *bf, uint32_t hash)
+{
+ const uint8_t *s = bf;
+ uint8_t c;
+
+ while ((c = *s++) != 0)
+ hash = hash * 33 + c; /* "perl": k=33, r=r+r/32 */
+ return (hash + (hash >> 5));
+}
+
+/*
+ * uint32_t
+ * hash32_strn(const void *bf, size_t len, uint32_t hash)
+ * return a 32 bit hash of NUL terminated ASCII string buf up to
+ * a maximum of len bytes,
+ * seeded with an initial hash value of hash (usually HASH32_STR_INIT).
+ */
+static __inline uint32_t
+hash32_strn(const void *bf, size_t len, uint32_t hash)
+{
+ const uint8_t *s = bf;
+ uint8_t c;
+
+ while ((c = *s++) != 0 && len-- != 0)
+ hash = hash * 33 + c; /* "perl": k=33, r=r+r/32 */
+ return (hash + (hash >> 5));
+}
+#endif /* __HAVE_HASH32_STR */
+
+
+#endif /* !_SYS_HASH_H_ */
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..e9de238
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/mmc_format.c b/mmc_format.c
new file mode 100644
index 0000000..464c023
--- /dev/null
+++ b/mmc_format.c
@@ -0,0 +1,1092 @@
+/* $NetBSD$ */
+
+/*
+ * File "mmc_format.c" is part of the UDFclient toolkit.
+ * File $Id: mmc_format.c,v 1.14 2015/08/05 18:26:29 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <inttypes.h>
+
+#include "uscsilib.h"
+
+
+/* globals */
+struct uscsi_dev dev;
+extern int scsilib_verbose;
+
+/* #define DEBUG(a) {a;} */
+#define DEBUG(a) ;
+
+
+uint64_t
+getmtime(void)
+{
+ struct timeval tp;
+
+ gettimeofday(&tp, NULL);
+ return (uint64_t) 1000000 * tp.tv_sec + tp.tv_usec;
+}
+
+
+void
+print_eta(uint32_t progress, uint64_t now, uint64_t start_time)
+{
+ int hours, minutes, seconds;
+ float fract;
+ uint32_t eta, ubusy;
+
+ if (progress == 0) {
+ printf(" ETA --:--:--");
+ return;
+ }
+ ubusy = (now - start_time);
+ fract = ((uint64_t) (0x10000) - progress) / progress;
+ eta = (fract * ubusy) / 1000000;
+
+ hours = (int) (eta/3600);
+ minutes = (int) (eta/60) % 60;
+ seconds = (int) eta % 60;
+ printf(" ETA %02d:%02d:%02d", hours, minutes, seconds);
+}
+
+
+void
+uscsi_waitop(struct uscsi_dev *dev)
+{
+ scsicmd cmd;
+ struct uscsi_sense sense;
+ uint64_t start_time;
+ uint32_t progress;
+ uint8_t buffer[256];
+ int asc, ascq;
+ int cnt = 0;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ bzero(buffer, sizeof(buffer));
+
+ /*
+ * not be to unpatient... give the drive some time to start or it
+ * might break off
+ */
+
+ start_time = getmtime();
+ sleep(10);
+
+ progress = 0;
+ while (progress < 0x10000) {
+ /* we need a command that is NOT going to stop the formatting */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0; /* test unit ready */
+ uscsi_command(SCSI_READCMD, dev,
+ cmd, 6, buffer, 0, 10000, &sense);
+
+ /*
+ * asc may be `not-ready' or `no-sense'. ascq for format in
+ * progress is 4 too
+ */
+ asc = sense.asc;
+ ascq = sense.ascq;
+ if (((asc == 0) && (ascq == 4)) || (asc == 4)) {
+ /* drive not ready : operation/format in progress */
+ if (sense.skey_valid) {
+ progress = sense.sense_key;
+ } else {
+ /* finished (? XXX) */
+ progress = 0x10000;
+ }
+ }
+ /* check if drive is ready again, ifso break out loop */
+ if ((asc == 0) && (ascq == 0)) {
+ progress = 0x10000;
+ }
+
+ printf("%3d %% ", (100 * progress / 0x10000));
+ printf("%c", "|/-\\" [cnt++ %4]); /* twirl */
+
+ /* print ETA */
+ print_eta(progress, getmtime(), start_time);
+
+ fflush(stdout);
+ sleep(1);
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ fflush(stdout);
+ }
+ printf("\n");
+
+ return;
+}
+
+
+static char
+*print_mmc_profile(int profile)
+{
+ static char scrap[100];
+
+ switch (profile) {
+ case 0x00 : return "Unknown[0] profile";
+ case 0x01 : return "Non removable disc";
+ case 0x02 : return "Removable disc";
+ case 0x03 : return "Magneto Optical with sector erase";
+ case 0x04 : return "Magneto Optical write once";
+ case 0x05 : return "Advance Storage Magneto Optical";
+ case 0x08 : return "CD-ROM";
+ case 0x09 : return "CD-R recordable";
+ case 0x0a : return "CD-RW rewritable";
+ case 0x10 : return "DVD-ROM";
+ case 0x11 : return "DVD-R sequential";
+ case 0x12 : return "DVD-RAM rewritable";
+ case 0x13 : return "DVD-RW restricted overwrite";
+ case 0x14 : return "DVD-RW sequential";
+ case 0x1a : return "DVD+RW rewritable";
+ case 0x1b : return "DVD+R recordable";
+ case 0x20 : return "DDCD readonly";
+ case 0x21 : return "DDCD-R recordable";
+ case 0x22 : return "DDCD-RW rewritable";
+ case 0x2b : return "DVD+R double layer";
+ case 0x40 : return "BD-ROM";
+ case 0x41 : return "BD-R Sequential Recording (SRM)";
+ case 0x42 : return "BD-R Random Recording (RRM)";
+ case 0x43 : return "BD-RE rewritable";
+ }
+ sprintf(scrap, "Reserved profile 0x%02x", profile);
+ return scrap;
+}
+
+
+int
+uscsi_get_mmc_profile(struct uscsi_dev *dev, int *mmc_profile)
+{
+ scsicmd cmd;
+ uint8_t buf[32];
+ int error;
+
+ *mmc_profile = 0;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x46; /* Get configuration */
+ cmd[ 8] = 32; /* just a small buffer size */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, dev, cmd, 10, buf, 32, 30000, NULL);
+ if (!error) {
+ *mmc_profile = buf[7] | (buf[6] << 8);
+ }
+
+ return error;
+}
+
+
+#define max_blk_len 10000
+int
+uscsi_set_packet_parameters(struct uscsi_dev *dev, int blockingnr)
+{
+ scsicmd cmd;
+ int val_len;
+ uint8_t res[max_blk_len], *pos;
+ int error;
+
+ /* Set up CD/DVD recording parameters */
+ DEBUG(printf("Setting device's recording parameters\n"));
+
+ val_len = 0x32+2+8;
+ bzero(res, val_len);
+
+ pos = res + 8;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ pos[ 0] = 0x05; /* page code 5 : cd writing */
+ pos[ 1] = 0x32; /* length in bytes */
+ pos[ 2] = 0; /* write type 0 : packet/incremental */
+
+ /* next session OK, data packet, rec. incr. fixed packets */
+ pos[ 3] = (3<<6) | 32 | 5;
+ pos[ 4] = 10; /* ISO mode 2; XA form 1 */
+ pos[ 8] = 0x20; /* CD-ROM XA disc or DDCD disc */
+ pos[10] = (blockingnr >> 24) & 0xff; /* MSB packet size */
+ pos[11] = (blockingnr >> 16) & 0xff;
+ pos[12] = (blockingnr >> 8) & 0xff;
+ pos[13] = (blockingnr ) & 0xff; /* LSB packet size */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x55; /* MODE SELECT (10) */
+ cmd[1] = 16; /* PF format */
+ cmd[7] = val_len >> 8; /* length of blob */
+ cmd[8] = val_len & 0xff;
+ cmd[9] = 0; /* control */
+
+ error = uscsi_command(SCSI_WRITECMD, dev,
+ cmd, 10, res, val_len, 30000, NULL);
+ if (error) {
+ perror("While WRTITING parameter page 5");
+ return error;
+ }
+
+ /* flag OK */
+ return 0;
+}
+#undef max_blk_len
+
+
+int
+get_format_capabilities(struct uscsi_dev *dev, uint8_t *buf, uint32_t *len)
+{
+ scsicmd cmd;
+ int list_length;
+ uint32_t buf_len = 512, trans_len;
+ int error;
+
+ assert(*len >= buf_len);
+ bzero(buf, buf_len);
+
+ trans_len = 12; /* only fixed header first */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x23; /* Read format capabilities */
+ cmd[7] = trans_len >> 8; /* MSB allocation length */
+ cmd[8] = trans_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, dev,
+ cmd, 10, buf, trans_len, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading format capabilities : %s\n",
+ strerror(error));
+ return error;
+ }
+
+ list_length = buf[ 3];
+
+ if (list_length % 8) {
+ printf( "\t\tWarning: violating SCSI spec,"
+ "capacity list length ought to be multiple of 8\n");
+ printf("\t\tInterpreting as including header of 4 bytes\n");
+ assert(list_length % 8 == 4);
+ list_length -= 4;
+ }
+
+ /* read in full capacity list */
+ trans_len = 12 + list_length; /* complete structure */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x23; /* Read format capabilities */
+ cmd[7] = trans_len >> 8; /* MSB allocation length */
+ cmd[8] = trans_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, dev,
+ cmd, 10, buf, trans_len, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading format capabilities : %s\n",
+ strerror(error));
+ return error;
+ }
+
+ *len = list_length;
+ return 0;
+}
+
+
+void
+print_format(int format_tp, uint32_t num_blks, uint32_t param,
+ int dscr_type, int verbose, int *supported)
+{
+ char *format_str, *nblks_str, *param_str, *user_spec;
+
+ format_str = nblks_str = param_str = "reserved";
+ user_spec = "";
+ *supported = 1;
+
+ switch (format_tp) {
+ case 0x00 :
+ format_str = "full format capacity";
+ nblks_str = "sectors";
+ param_str = "block length in bytes";
+ user_spec = "'-F [-b blockingnr]'";
+ break;
+ case 0x01 :
+ format_str = "spare area expansion";
+ nblks_str = "extension in blocks";
+ param_str = "block length in bytes";
+ user_spec = "'-S'";
+ break;
+ /* 0x02 - 0x03 reserved */
+ case 0x04 :
+ format_str = "variable length zone'd format";
+ nblks_str = "zone length";
+ param_str = "zone number";
+ *supported = 0;
+ break;
+ case 0x05 :
+ format_str = "fixed length zone'd format";
+ nblks_str = "zone length";
+ param_str = "last zone number";
+ *supported = 0;
+ break;
+ /* 0x06 - 0x0f reserved */
+ case 0x10 :
+ format_str = "CD-RW/DVD-RW full packet format";
+ nblks_str = "adressable blocks";
+ param_str = "fixed packet size/ECC blocksize in sectors";
+ user_spec = "'-F -p [-b blockingnr]'";
+ break;
+ case 0x11 :
+ format_str = "CD-RW/DVD-RW grow session";
+ nblks_str = "adressable blocks";
+ param_str = "fixed packet size/ECC blocksize in sectors";
+ user_spec = "'-G'";
+ break;
+ case 0x12 :
+ format_str = "CD-RW/DVD-RW add session";
+ nblks_str = "adressable blocks";
+ param_str = "maximum fixed packet size/ECC blocksize "
+ "in sectors";
+ *supported = 0;
+ break;
+ case 0x13 :
+ format_str = "DVD-RW max growth of last complete session";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ user_spec = "'-G'";
+ break;
+ case 0x14 :
+ format_str = "DVD-RW quick grow last session";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ *supported = 0;
+ break;
+ case 0x15 :
+ format_str = "DVD-RW quick full format";
+ nblks_str = "adressable blocks";
+ param_str = "ECC blocksize in sectors";
+ *supported = 0;
+ break;
+ /* 0x16 - 0x23 reserved */
+ case 0x24 :
+ format_str = "background MRW format";
+ nblks_str = "Defect Management Area blocks";
+ param_str = "not used";
+ user_spec = "'[-R] [-s] [-w] -F -M [-b blockingnr]'";
+ break;
+ /* 0x25 reserved */
+ case 0x26 :
+ format_str = "background DVD+RW full format";
+ nblks_str = "sectors";
+ param_str = "not used";
+ user_spec = "'[-R] [-w] -F'";
+ break;
+ /* 0x27 - 0x2f reserved */
+ case 0x30 :
+ format_str = "BD-RE full format with spare area";
+ nblks_str = "blocks";
+ param_str = "total spare area size in clusters";
+ user_spec = "'[-s] -F'";
+ break;
+ case 0x31 :
+ format_str = "BD-RE full format without spare area";
+ nblks_str = "blocks";
+ param_str = "block length in bytes";
+ user_spec = "'-F'";
+ break;
+ /* 0x32 - 0x3f reserved */
+ default :
+ break;
+ }
+
+ if (verbose) {
+ printf("\n\tFormat type 0x%02x : %s\n", format_tp, format_str);
+
+ switch (dscr_type) {
+ case 1 :
+ printf( "\t\tUnformatted media,"
+ "maximum formatted capacity\n");
+ break;
+ case 2 :
+ printf( "\t\tFormatted media,"
+ "current formatted capacity\n");
+ break;
+ case 3 :
+ printf( "\t\tNo media present or incomplete session, "
+ "maximum formatted capacity\n");
+ break;
+ default :
+ printf("\t\tUnspecified descriptor type\n");
+ break;
+ }
+
+ printf("\t\tNumber of blocks : %12d\t(%s)\n",
+ num_blks, nblks_str);
+ printf("\t\tParameter : %12d\t(%s)\n",
+ param, param_str);
+
+ if (format_tp == 0x24) {
+ printf( "\t\tExpert select : "
+ "'-X 0x%02x:0xffffff:0' or "
+ "'-X 0x%02x:0xffff0000:0'\n",
+ format_tp, format_tp);
+ } else {
+ printf( "\t\tExpert select : "
+ "'-X 0x%02x:%d:%d'\n",
+ format_tp, num_blks, param);
+ }
+ if (*supported) {
+ printf("\t\tmmc_format arg : %s\n", user_spec);
+ } else {
+ printf("\t\t** not supported **\n");
+ }
+ }
+}
+
+
+void
+process_format_caps(uint8_t *buf, int list_length, int verbose,
+ uint8_t *allow, uint32_t *blks, uint32_t *params)
+{
+ uint32_t num_blks, param;
+ uint8_t *fcd;
+ int dscr_type, format_tp;
+ int supported;
+
+ bzero(allow, 255);
+ bzero(blks, 255*4);
+ bzero(params, 255*4);
+
+ fcd = buf + 4;
+ list_length -= 4; /* strip header */
+
+ if (verbose)
+ printf("\tCurrent/max capacity followed by additional capacity,"
+ "reported length of %d bytes (8/entry)\n", list_length);
+
+ while (list_length > 0) {
+ num_blks = fcd[ 3] | (fcd[ 2] << 8) |
+ (fcd[ 1] << 16) | (fcd[ 0] << 24);
+ dscr_type = fcd[ 4] & 3;
+ format_tp = fcd[ 4] >> 2;
+ param = fcd[ 7] | (fcd[ 6] << 8) | (fcd[ 5] << 16);
+
+ print_format(format_tp, num_blks, param, dscr_type, verbose,
+ &supported);
+
+ allow[format_tp] = 1; /* TODO = supported? */
+ blks[format_tp] = num_blks;
+ params[format_tp] = param;
+
+ fcd += 8;
+ list_length-=8;
+ }
+}
+
+
+
+/* format a CD-RW disc */
+/* old style format 7 */
+int
+uscsi_format_cdrw_mode7(struct uscsi_dev *dev, uint32_t blocks)
+{
+ scsicmd cmd;
+ struct uscsi_sense sense;
+ uint32_t param;
+ uint8_t buffer[16];
+ int cnt, error;
+
+ param = cnt = 0;
+
+ if (blocks % 32) {
+ blocks -= blocks % 32;
+ }
+
+ bzero(cmd, SCSI_CMD_LEN);
+ bzero(buffer, sizeof(buffer));
+
+ cmd[0] = 0x04; /* format unit */
+ cmd[1] = 0x17; /* parameter list format 7 follows */
+ cmd[5] = 0; /* control */
+
+ /* format list header */
+ buffer[ 0] = 0; /* reserved */
+ buffer[ 1] = 0x80 | 0x02; /* Valid info, immediate return */
+ buffer[ 2] = 0; /* MSB format descriptor length */
+ buffer[ 3] = 8; /* LSB ... */
+
+ /*
+ * for CD-RW the initialisation pattern bit is reserved, but there IS
+ * one
+ */
+
+ buffer[ 4] = 0; /* no header */
+ buffer[ 5] = 0; /* default pattern */
+ buffer[ 6] = 0; /* pattern length MSB */
+ buffer[ 7] = 0; /* pattern length LSB */
+
+ /* 8 bytes of format descriptor */
+ /* (s)ession bit 1<<7, (g)row bit 1<<6 */
+ /* SG action */
+ /* 00 format disc with number of user data blocks */
+ /* 10 create new session with number of data blocks */
+ /* x1 grow session to be number of data blocks */
+
+ buffer[ 8] = 0x00; /* session and grow bits (7 and 6) */
+ buffer[ 9] = 0; /* reserved */
+ buffer[10] = 0; /* reserved */
+ buffer[11] = 0; /* reserved */
+ buffer[12] = (blocks >> 24) & 0xff; /* blocks MSB */
+ buffer[13] = (blocks >> 16) & 0xff;
+ buffer[14] = (blocks >> 8) & 0xff;
+ buffer[15] = (blocks ) & 0xff; /* blocks LSB */
+
+ /* this will take a while .... */
+ error = uscsi_command(SCSI_WRITECMD, dev,
+ cmd, 6, buffer, sizeof(buffer), UINT_MAX, &sense);
+ if (error)
+ return error;
+
+ uscsi_waitop(dev);
+ return 0;
+}
+
+
+int
+uscsi_format_disc(struct uscsi_dev *dev, int immed, int format_type,
+ uint32_t blocks, uint32_t param, int certification, int cmplist)
+{
+ scsicmd cmd;
+ struct uscsi_sense sense;
+ uint8_t buffer[16], fmt_flags;
+ int error;
+
+ fmt_flags = 0x80; /* valid info flag */
+ if (immed)
+ fmt_flags |= 2;
+ if (certification == 0)
+ fmt_flags |= 32;
+
+ if (cmplist)
+ cmplist = 8;
+
+#if 0
+ if (mmc_profile != 0x43) {
+ /* certification specifier only valid for BD-RE */
+ certification = 0;
+ }
+#endif
+
+ bzero(cmd, SCSI_CMD_LEN);
+ bzero(buffer, sizeof(buffer));
+
+ cmd[0] = 0x04; /* format unit */
+ cmd[1] = 0x11 | cmplist; /* parameter list format 1 follows */
+ cmd[5] = 0; /* control */
+
+ /* format list header */
+ buffer[ 0] = 0; /* reserved */
+ buffer[ 1] = 0x80 | fmt_flags; /* Valid info, flags follow */
+ buffer[ 2] = 0; /* MSB format descriptor length */
+ buffer[ 3] = 8; /* LSB ... */
+
+ /* 8 bytes of format descriptor */
+ buffer[ 4] = (blocks >> 24) & 0xff; /* blocks MSB */
+ buffer[ 5] = (blocks >> 16) & 0xff;
+ buffer[ 6] = (blocks >> 8) & 0xff;
+ buffer[ 7] = (blocks ) & 0xff; /* blocks LSB */
+ buffer[ 8] = (format_type << 2) | certification;
+ buffer[ 9] = (param >> 16) & 0xff; /* parameter MSB */
+ buffer[10] = (param >> 8) & 0xff; /* packet size */
+ buffer[11] = (param ) & 0xff; /* parameter LSB */
+
+ /* this will take a while .... */
+ error = uscsi_command(SCSI_WRITECMD, dev,
+ cmd, 6, buffer, 12, UINT_MAX, &sense);
+ if (error)
+ return error;
+
+ if (immed)
+ uscsi_waitop(dev);
+
+ return 0;
+}
+
+
+int
+uscsi_blank_disc(struct uscsi_dev *dev)
+{
+ scsicmd cmd;
+ int error;
+
+ /* XXX check if the device can blank! */
+
+
+ /* blank disc */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0xA1; /* blank */
+ cmd[ 1] = 16; /* Immediate, blank complete */
+ cmd[11] = 0; /* control */
+
+ /* this will take a while .... */
+ error = uscsi_command(SCSI_WRITECMD, dev,
+ cmd, 12, NULL, 0, UINT_MAX, NULL);
+ if (error)
+ return error;
+
+ uscsi_waitop(dev);
+ return 0;
+}
+
+
+int
+usage(char *program)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Usage: %s [options] devicename\n", program);
+ fprintf(stderr,
+ "-B blank cd-rw disc before formatting\n"
+ "-F format cd-rw disc\n"
+ "-O CD-RW formatting 'old-style' for old CD-RW drives\n"
+ "-M select MRW format\n"
+ "-R restart MRW & DVD+RW format\n"
+ "-G grow last CD-RW/DVD-RW session\n"
+ "-S grow spare space DVD-RAM/BD-RE\n"
+ "-s format DVD+MRW/BD-RE with extra spare space\n"
+ "-w wait until completion of background format\n"
+ "-p explicitly set packet format\n"
+ "-c num media certification for DVD-RAM/BD-RE : "
+ "0 no, 1 full, 2 quick\n"
+ "-r recompile defect list for DVD-RAM (cmplist)\n"
+ "-h -H -I help/inquiry formats\n"
+ "-X format expert format selector form 'fmt:blks:param' with -c\n"
+ "-b blockingnr in sectors (for CD-RW)\n"
+ "-D verbose SCSI command errors\n"
+ );
+ return 1;
+}
+
+
+extern char *optarg;
+extern int optind;
+extern int optreset;
+
+
+#define max_caps_len 512
+int
+main(int argc, char *argv[])
+{
+ struct uscsi_addr saddr;
+ uint32_t caps_len = max_caps_len;
+ uint32_t blks[256], params[256];
+ uint32_t format_type, format_blks, format_param, blockingnr;
+ uint8_t allow[256];
+ uint8_t caps[max_caps_len];
+ char *progname;
+ int blank, format, mrw, background;
+ int inquiry, spare, oldtimer;
+ int expert;
+ int restart_format, grow_session, grow_spare, packet_wr;
+ int mmc_profile, flag, error, display_usage;
+ int certification, cmplist;
+ int wait_until_finished;
+ progname = strdup(argv[0]);
+ if (argc == 1) {
+ return usage(progname);
+ }
+
+ blank = 0;
+ format = 0;
+ mrw = 0;
+ restart_format = 0;
+ grow_session = 0;
+ grow_spare = 0;
+ wait_until_finished = 0;
+ packet_wr = 0;
+ certification = 1;
+ cmplist = 0;
+ inquiry = 0;
+ spare = 0;
+ inquiry = 0;
+ oldtimer = 0;
+ expert = 0;
+ display_usage = 0;
+ blockingnr = 32;
+ uscsilib_verbose = 0;
+ while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
+ switch (flag) {
+ case 'B' :
+ blank = 1;
+ break;
+ case 'F' :
+ format = 1;
+ break;
+ case 'M' :
+ mrw = 1;
+ break;
+ case 'R' :
+ restart_format = 1;
+ break;
+ case 'G' :
+ grow_session = 1;
+ break;
+ case 'S' :
+ grow_spare = 1;
+ break;
+ case 'w' :
+ wait_until_finished = 1;
+ break;
+ case 'p' :
+ packet_wr = 1;
+ break;
+ case 's' :
+ spare = 1;
+ break;
+ case 'c' :
+ certification = atoi(optarg);
+ break;
+ case 'r' :
+ cmplist = 1;
+ break;
+ case 'h' :
+ case 'H' :
+ display_usage = 1;
+ case 'I' :
+ inquiry = 1;
+ break;
+ case 'X' :
+ /* TODO parse expert mode string */
+ printf("-X not implemented yet\n");
+ expert = 1;
+ exit(1);
+ break;
+ case 'O' :
+ /* oldtimer CD-RW format */
+ oldtimer = 1;
+ format = 1;
+ break;
+ case 'b' :
+ blockingnr = atoi(optarg);
+ break;
+ case 'D' :
+ uscsilib_verbose = 1;
+ break;
+ default :
+ return usage(progname);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if ((!blank && !format && !grow_session && !grow_spare) &&
+ (!expert && !inquiry)) {
+ fprintf(stderr, "%s : at least one of -B, -F, -G, -S, -X or -I "
+ "needs to be specified\n\n", progname);
+ return usage(progname);
+ }
+
+ if (format + grow_session + grow_spare + expert > 1) {
+ fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
+ "needs to be specified\n\n", progname);
+ return usage(progname);
+ }
+
+ if (argc != 1) return usage(progname);
+
+ /* Open the device */
+ dev.dev_name = strdup(*argv);
+ printf("Opening device %s\n", dev.dev_name);
+ error = uscsi_open(&dev);
+ if (error) {
+ fprintf(stderr, "Device failed to open : %s\n",
+ strerror(error));
+ exit(1);
+ }
+
+ error = uscsi_check_for_scsi(&dev);
+ if (error) {
+ fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
+ strerror(error));
+ exit(1);
+ }
+
+ error = uscsi_identify(&dev, &saddr);
+ if (error) {
+ fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
+ strerror(error));
+ exit(1);
+ }
+
+ printf("\nDevice identifies itself as : ");
+
+ if (saddr.type == USCSI_TYPE_SCSI) {
+ printf("SCSI busnum = %d, target = %d, lun = %d\n",
+ saddr.addr.scsi.scbus, saddr.addr.scsi.target,
+ saddr.addr.scsi.lun);
+ } else {
+ printf("ATAPI busnum = %d, drive = %d\n",
+ saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
+ }
+
+ printf("\n");
+
+ /* get MMC profile */
+ error = uscsi_get_mmc_profile(&dev, &mmc_profile);
+ if (error) {
+ fprintf(stderr,
+ "Can't get the disc's MMC profile because of :"
+ " %s\n", strerror(error));
+ fprintf(stderr, "aborting\n");
+ uscsi_close(&dev);
+ return 1;
+ }
+
+ /* blank disc section */
+ if (blank) {
+ printf("\nBlanking disc.... "); fflush(stdout);
+ error = uscsi_blank_disc(&dev);
+
+ if (error) {
+ printf("fail\n"); fflush(stdout);
+ fprintf(stderr,
+ "Blanking failed because of : %s\n",
+ strerror(error));
+ uscsi_close(&dev);
+
+ return 1;
+ } else {
+ printf("success!\n\n");
+ }
+ }
+
+ /* re-get MMC profile */
+ error = uscsi_get_mmc_profile(&dev, &mmc_profile);
+ if (error) {
+ fprintf(stderr,
+ "Can't get the disc's MMC profile because of : %s\n",
+ strerror(error));
+ fprintf(stderr, "aborting\n");
+ uscsi_close(&dev);
+ return 1;
+ }
+
+ error = get_format_capabilities(&dev, caps, &caps_len);
+ if (error)
+ exit(1);
+
+ process_format_caps(caps, caps_len, inquiry, allow, blks, params);
+
+ if (!format && !expert && inquiry) {
+ /* we're done */
+ if (display_usage)
+ usage(progname);
+ uscsi_close(&dev);
+ exit(0);
+ }
+
+ format_type = 0;
+ /* expert format section */
+ if (expert) {
+ }
+
+ /* normal format section */
+ if (format) {
+ /* get current mmc profile of disc */
+
+ if (oldtimer && mmc_profile != 0x0a) {
+ printf("Oldtimer flag only defined for CD-RW; "
+ "ignored\n");
+ }
+
+ switch (mmc_profile) {
+ case 0x12 : /* DVD-RAM */
+ format_type = 0x00;
+ break;
+ case 0x0a : /* CD-RW */
+ format_type = mrw ? 0x24 : 0x10;
+ packet_wr = 1;
+ break;
+ case 0x13 : /* DVD-RW restricted overwrite */
+ case 0x14 : /* DVD-RW sequential */
+ format_type = 0x10;
+ /*
+ * Some drives suddenly stop supporting this format
+ * type when packet_wr = 1
+ */
+ packet_wr = 0;
+ break;
+ case 0x1a : /* DVD+RW */
+ format_type = mrw ? 0x24 : 0x26;
+ break;
+ case 0x43 : /* BD-RE */
+ format_type = spare ? 0x30 : 0x31;
+ break;
+ default :
+ fprintf(stderr, "Can't format discs of type %s\n",
+ print_mmc_profile(mmc_profile));
+ uscsi_close(&dev);
+ exit(1);
+ }
+ }
+
+ if (grow_spare) {
+ switch (mmc_profile) {
+ case 0x12 : /* DVD-RAM */
+ case 0x43 : /* BD-RE */
+ format_type = 0x01;
+ break;
+ default :
+ fprintf(stderr,
+ "Can't grow spare area for discs of type %s\n",
+ print_mmc_profile(mmc_profile));
+ uscsi_close(&dev);
+ exit(1);
+ }
+ }
+
+ if (grow_session) {
+ switch (mmc_profile) {
+ case 0x0a : /* CD-RW */
+ format_type = 0x11;
+ break;
+ case 0x13 : /* DVD-RW restricted overwrite */
+ case 0x14 : /* DVD-RW sequential ? */
+ format_type = 0x13;
+ break;
+ default :
+ uscsi_close(&dev);
+ fprintf(stderr,
+ "Can't grow session for discs of type %s\n",
+ print_mmc_profile(mmc_profile));
+ exit(1);
+ }
+ }
+
+ /* check if format type is allowed */
+ format_blks = blks[format_type];
+ format_param = params[format_type];
+ if (!allow[format_type]) {
+ if (!inquiry)
+ process_format_caps(caps, caps_len, 1, allow,
+ blks, params);
+
+ printf("\n");
+ fflush(stdout);
+ fprintf(stderr,
+ "Drive indicates it can't format with deduced format "
+ "type 0x%02x\n", format_type);
+ uscsi_close(&dev);
+ exit(1);
+ }
+
+ if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
+ {
+ fprintf(stderr,
+ "Format restarting only for MRW formats or DVD+RW "
+ "formats\n");
+ uscsi_close(&dev);
+ exit(1);
+ }
+
+ if (restart_format && !wait_until_finished) {
+ printf( "Warning : format restarting without waiting for it be "
+ "finished is prolly not handy\n");
+ }
+
+ /* explicitly select packet write just in case */
+ if (packet_wr) {
+ printf("Explicitly setting packet type and blocking number\n");
+ error = uscsi_set_packet_parameters(&dev, blockingnr);
+ if (error) {
+ fprintf(stderr,
+ "Can't set packet writing and blocking number: "
+ "%s\n", strerror(error));
+ uscsi_close(&dev);
+ exit(1);
+ }
+ }
+
+ /* determine if formatting is done in the background */
+ background = 0;
+ if (format_type == 0x24) background = 1;
+ if (format_type == 0x26) background = 1;
+
+ /* special case format type 0x24 : MRW */
+ if (format_type == 0x24) {
+ format_blks = spare ? 0xffff0000 : 0xffffffff;
+ format_param = restart_format;
+ }
+ /* special case format type 0x26 : DVD+RW */
+ if (format_type == 0x26) {
+ format_param = restart_format;
+ }
+
+ /* verbose to the user */
+ DEBUG(
+ printf("Actual format selected: "
+ "format_type 0x%02x, blks %d, param %d, "
+ "certification %d, cmplist %d\n",
+ format_type, format_blks, format_param,
+ certification, cmplist);
+ );
+ printf("\nFormatting.... "); fflush(stdout);
+
+ /* formatting time! */
+ if (oldtimer) {
+ error = uscsi_format_cdrw_mode7(&dev, format_blks);
+ background = 0;
+ } else {
+ error = uscsi_format_disc(&dev, !background, format_type,
+ format_blks, format_param, certification,
+ cmplist);
+ }
+
+ /* what now? */
+ if (error) {
+ printf("fail\n"); fflush(stdout);
+ fprintf(stderr, "Formatting failed because of : %s\n",
+ strerror(error));
+ } else {
+ if (background) {
+ printf("background formatting in progress\n");
+ if (wait_until_finished) {
+ printf("Waiting for completion ... ");
+ uscsi_waitop(&dev);
+ }
+ /* explicitly do NOT close disc ... (for now) */
+ return 0;
+ } else {
+ printf("success!\n\n");
+ }
+ }
+
+ /* finish up */
+ uscsi_close(&dev);
+
+ return error;
+}
+#undef max_caps_len
+
diff --git a/newfs_udf.c b/newfs_udf.c
new file mode 100644
index 0000000..de340bf
--- /dev/null
+++ b/newfs_udf.c
@@ -0,0 +1,707 @@
+/* $NetBSD$ */
+
+/*
+ * File "newfs_udf.c" is part of the UDFclient toolkit.
+ * File $Id: newfs_udf.c,v 1.44 2015/08/05 18:26:29 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include "udf.h"
+#include "udf_bswap.h"
+
+
+/* switches */
+
+/* #define DEBUG(a) (a) */
+#define DEBUG(a) if (0) { a; }
+
+
+/* include the dump parts ... in order to get a more sane splitting */
+extern void udf_dump_descriptor(union dscrptr *dscrpt);
+extern void udf_dump_alive_sets(void);
+extern void udf_dump_root_dir(struct udf_mountpoint *mountpoint);
+extern void udf_dump_discinfo(struct udf_discinfo *disc);
+
+
+/* globals */
+extern int udf_verbose;
+extern int uscsilib_verbose;
+
+
+/* UDF library dependencies for writing */
+int udf_add_session_to_discinfo(struct udf_discinfo *disc, int session, struct anchor_vdp *avdp, int error);
+int udf_stop_writing_threads(struct udf_discinfo *disc);
+int udf_get_volumeset_space(struct udf_discinfo *disc);
+
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/*
+ * newfs
+ *
+ * Simple-UDF-disc-types recognized by this newfs program :
+ */
+
+#define DISC_TYPE_NORMAL 0
+#define DISC_TYPE_VIRTUAL 1
+#define DISC_TYPE_SPARABLE 2
+#define DISC_TYPE_META 3
+
+
+
+void newfs_test_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) {
+#if 0
+ DEBUG(
+ printf("WRcallback called with sector data %p\n", sectordata);
+ printf("\treason %d\n", reason);
+ printf("\toffset %08d\n", (int) wrcallback->offset);
+ printf("\tlb_num %08d\n", (int) wrcallback->lb_num);
+ printf("\tlength %08d\n", (int) wrcallback->length);
+ printf("\terror %d\n", (int) error);
+ );
+#else
+ reason = reason;
+ wrcallback = wrcallback;
+ sectordata = sectordata;
+#endif
+ if (error) {
+ fprintf(stderr, "HELP! got writing error while writing; can't fix yet! (%s)\n", strerror(error));
+ return;
+ }
+}
+
+
+void writeout_vds(struct udf_session *udf_session, uint16_t dscr_ver) {
+ struct udf_wrcallback wrcallback;
+ struct vrs_desc *vrs_desc;
+ uint32_t pos, sector_size, dpos;
+ uint32_t cnt;
+
+ sector_size = udf_session->disc->sector_size;
+ wrcallback.function = newfs_test_callback;
+
+ /* Build ISO/Ecma-167 volume recognition sequence */
+ vrs_desc = calloc(1, 64*1024); assert(vrs_desc); /* working copy of one max sized ISO descriptor */
+
+ /* white out VRS */
+ pos = ((32*1024 + sector_size - 1) / sector_size); /* definition: first sector AFTER 32kb, minimum sector size 2048 */
+ dpos = (2048 + sector_size - 1) / sector_size;
+ for (cnt = 0; cnt < 6*dpos; cnt++) {
+ udf_write_session_sector(udf_session, pos + cnt, "blank VRS", (uint8_t *) vrs_desc, 0, &wrcallback);
+ }
+
+ /* write out VRS */
+ vrs_desc->struct_type = 0;
+ vrs_desc->version = 1;
+
+ pos = ((32*1024 + sector_size - 1) / sector_size); /* definition: first sector AFTER 32kb + descr*2048 */
+
+#if 0
+ /* CD001 identifies bridge disc with ISO 9660 */
+ memcpy(vrs_desc->identifier, VRS_CD001, 5);
+ udf_write_session_sector(udf_session, pos, "VRS CD001", (uint8_t *) vrs_desc, 0, &wrcallback);
+ pos += dpos;
+#else
+ memcpy(vrs_desc->identifier, VRS_BEA01, 5);
+ udf_write_session_sector(udf_session, pos, "VRS BEA01", (uint8_t *) vrs_desc, 0, &wrcallback);
+ pos += dpos;
+#endif
+ if (dscr_ver == 2) {
+ memcpy(vrs_desc->identifier, VRS_NSR02, 5);
+ } else {
+ memcpy(vrs_desc->identifier, VRS_NSR03, 5);
+ }
+ udf_write_session_sector(udf_session, pos, "VRS NSR[23]", (uint8_t *) vrs_desc, 0, &wrcallback);
+ pos += dpos;
+
+ memcpy(vrs_desc->identifier, VRS_TEA01, 5);
+ udf_write_session_sector(udf_session, pos, "VRS TEA01", (uint8_t *) vrs_desc, 0, &wrcallback);
+ pos += dpos;
+ /* followed by at least one blank block blanked up */
+
+ free(vrs_desc);
+}
+
+
+/*
+ * newfs_udf creates a new UDF filingsystem on a formatted disc. It is
+ * restricted for now only create `simple' i.e. UDF specification discs.
+ *
+ * For now, only CD-RW/DVD+RW filingsystems are implemented.
+ *
+ * FIXME we ought to determine UDF version numbers in advance so it can
+ * easiliy be incorporated when nessisary.
+ *
+ * XXX allocation of space could be done smarter XXX
+ */
+
+void newfs_udf(struct udf_discinfo *disc, uint16_t dscr_ver, char *volset_name, char *privol_name, char *logvol_name, int vds_num, int max_vol_seq, uint32_t lb_mult, int udf_type) {
+ struct long_ad *fsd_loc;
+ struct pri_vol_desc *primary;
+ struct anchor_vdp *anchor;
+ struct desc_tag *terminator;
+ struct part_desc *partition;
+ struct logvol_desc *logvol;
+ struct unalloc_sp_desc *unallocsp;
+ struct impvol_desc *implvol;
+ struct space_bitmap_desc *unalloc_space_bitmap_descr;
+ struct fileset_desc *fileset;
+ struct udf_session *udf_session;
+ struct udf_pri_vol *udf_pri_vol;
+ struct udf_log_vol *udf_log_vol;
+ struct udf_partition *udf_partition;
+ struct udf_mountpoint *udf_mountpoint;
+ struct udf_node *root_node, *dummy_node;
+ struct udf_allocentry *dscr_entry;
+ struct udf_wrcallback wrcallback;
+ uint32_t bits;
+ uint32_t bytes;
+ uint32_t offset, pos, sector_size, lb_size;
+ uint32_t anchor0, anchor1, anchor2, anchor2_rel;
+ uint32_t lvd_length, lvd1_area, lvd2_area, end_lvd;
+ uint32_t part_start, part_length, integrity_start, integrity_length;
+ uint32_t unalloc_space_bitmap, space_bitmap_size;
+ uint32_t start_lb_fsd, num_lbs_fsd;
+ uint16_t serial, *udfver_pos, vpart_fsd;
+ uint8_t *blank;
+ uint32_t cnt;
+ int error;
+
+ if (!disc->recordable) {
+ fprintf(stderr, "Can't create filingsystem on a non recordable disc\n");
+ return;
+ }
+
+ if (disc->sequential) {
+ /* for sequential discs, close last track when nessisary */
+ fprintf(stderr, "No support yet for creating filingsystem on sequential recordables\n");
+ /* TODO recordable format */
+ return;
+ }
+ if (!disc->sequential && !disc->rewritable) {
+ /* non sequential WORM; not tested, no specimen */
+ fprintf(stderr, "No support yet for non-sequential WORM devices\n");
+ /* TODO non sequential WORM format */
+ return;
+ }
+
+ /* TODO reuse parts of rewritable formatting for other types */
+
+ if (disc->rewritable) {
+ /* plain rewritable CD-RW or DVD+RW/DVD-RW, file etc. */
+ switch (disc->disc_state) {
+ case DISC_STATE_EMPTY:
+ fprintf(stderr, "Disc is empty; please packet-format it before use\n");
+ return;
+ default:
+ case DISC_STATE_INCOMPLETE :
+ fprintf(stderr, "Disc is not properly formatted; its interrupted at formatting time\n");
+ return;
+ case DISC_STATE_FULL :
+ case DISC_STATE_NOT_SERIAL :
+ /* OK */
+ break;
+ }
+
+ if (udf_discinfo_is_cd_or_dvd(disc) && disc->last_session_state != SESSION_STATE_COMPLETE) {
+ fprintf(stderr, "Disc is marked being not serial, full, but the last session is not marked closed; Most likely formatting problem, try formatting it again\n");
+ return;
+ }
+
+ if (disc->num_sessions > 1) {
+ fprintf(stderr, "Can't handle multiple session rewritable discs yet\n");
+ return;
+ }
+
+ /* rewritable format */
+ printf("Creating a filingsystem on a recordable rewritable CD-RW or DVD+RW/DVD-RW or fixed length file\n");
+
+ /* initialse statics */
+ bzero(&wrcallback, sizeof(struct udf_wrcallback));
+
+ STAILQ_INIT(&disc->sessions);
+
+ wrcallback.function = newfs_test_callback; /* NULL for no callbacks */
+/* XXX wrcallback.structure = (void *) 0xdeadbeef; */
+
+ /* setup recording */
+ disc->udf_recording = 1;
+ udf_discinfo_set_recording_parameters(disc, 0);
+
+ /* Set up disc space and create decscriptors */
+ sector_size = disc->sector_size;
+ lb_size = sector_size * lb_mult; /* express logical blocks as given multiple; normally 1 */
+
+ blank = calloc(1, lb_size);
+ assert(blank);
+
+ /* note that session_end[0] is the first sector NOT adressable so substract one */
+ serial = 0; /* primary starts at zero */
+ offset = 256; /* first offset at sector number 256 to allow for prepending loader etc. (use 512 for recordables) */
+ anchor0 = offset;
+ anchor1 = disc->session_end[0] - 1; /* only one session on rewritables */
+ anchor2 = disc->session_end[0] - 256 - 1; /* possible anchor, rather not use it on rewritables without sparables... */
+
+ lvd_length = MAX(UDF_READWRITE_LINE_LENGTH, disc->packet_size[0]);
+ lvd1_area = anchor0 + 1;
+ lvd2_area = lvd1_area + lvd_length;
+ end_lvd = lvd2_area + lvd_length;
+
+ /* insert logical volume integrity sequence space if there is a logical volume */
+ integrity_start = 0;
+ integrity_length = 0;
+ if (logvol_name) {
+ /*
+ * Maybe the space is a bit biggish but it means that
+ * 2 packet sized blocks can be scratched before the
+ * media needs to be reformatted. Minimum a line
+ * length to prevent multiple integrity descriptor
+ * writes to mess up other disc info.
+ */
+ integrity_start = end_lvd;
+
+ integrity_length = MAX(UDF_READWRITE_LINE_LENGTH, 2*disc->packet_size[0]);
+ assert(integrity_length * lb_size >= 8*1024); /* UDF req. */
+
+ end_lvd += integrity_length;
+ }
+
+ /* initial start of physical partion space */
+ part_start = end_lvd;
+
+ /* partition size can be relative on anchor1 or anchor2; if on anchor1, anchor2 needs to be allocated */
+ part_length = (sector_size * (uint64_t) (anchor2 - part_start)) / lb_size - 1;
+
+ /* reserve space for unallocated space bitmap */
+ bits = part_length;
+ bytes = (bits + 7)/8;
+ space_bitmap_size = (bytes + sizeof(struct space_bitmap_desc)-1);
+
+ /* round space bitmap size to sector size */
+ /* FIXME: space bitmap recording on disc sector sizes or on lb_sizes? */
+ space_bitmap_size = ((space_bitmap_size + lb_size - 1) / lb_size) * lb_size;
+
+ /* sanity check to see if it can be formatted */
+ if ((part_length <= 0) || (anchor1 <= 576)) { /* XXX for now XXX */
+ fprintf(stderr, "Too small a disc to be formatted with the UDF filingsystem\n");
+ return;
+ }
+
+ /* build the anchors */
+ error = udf_create_empty_anchor_volume_descriptor(sector_size, dscr_ver, lvd1_area, lvd2_area, lvd_length, &anchor);
+
+ /* get udf_session structure */
+ assert(disc->num_sessions == 1);
+ udf_add_session_to_discinfo(disc, 0, anchor, 0); /* inits complete session related structures */
+ udf_session = STAILQ_FIRST(&disc->sessions);
+ assert(udf_session);
+
+ /* create primary and partition descriptor */
+ unalloc_space_bitmap = 0; /* allocate it at the start */
+
+ error = udf_create_empty_primary_volume_descriptor(sector_size, dscr_ver, serial++, volset_name, privol_name, vds_num, max_vol_seq, &primary);
+ error = udf_create_empty_partition_descriptor(sector_size, dscr_ver, serial++, 0, UDF_ACCESSTYPE_OVERWRITABLE, part_start, part_length, space_bitmap_size, unalloc_space_bitmap, &partition);
+
+ /* process primary and partition */
+ udf_proc_pri_vol(udf_session, &udf_pri_vol, primary);
+ udf_proc_part(udf_pri_vol, &udf_partition, partition);
+
+ /* all space in the partition is marked `free' at start */
+ udf_mark_allocentry_queue(&udf_partition->unalloc_space_queue, lb_size, 0, (uint64_t) part_length * lb_size, UDF_SPACE_FREE, NULL, NULL);
+ udf_partition->free_unalloc_space = (uint64_t) part_length * lb_size;
+
+ /* create unallocated space descriptor and fill in its use in the partition space */
+ /* (could be done a bit smarter) */
+ error = udf_create_empty_space_bitmap(lb_size, dscr_ver, part_length, &unalloc_space_bitmap_descr);
+ udf_partition->unalloc_space_bitmap = unalloc_space_bitmap_descr;
+
+ udf_mark_allocentry_queue(&udf_partition->unalloc_space_queue, lb_size, (uint64_t) unalloc_space_bitmap * lb_size, space_bitmap_size, UDF_SPACE_ALLOCATED, NULL, NULL);
+ udf_partition->free_unalloc_space -= space_bitmap_size;
+
+ anchor2_rel = (anchor2*sector_size - part_start*sector_size) / lb_size;
+ if (0) {
+ if (anchor2_rel <= part_length) {
+ /* overlap with anchor2 -> mark it in the unallocated space DESCRIPTOR, not in the unallocated space bitmap */
+ udf_mark_allocentry_queue(&udf_partition->unalloc_space_queue, lb_size, (uint64_t) anchor2_rel * lb_size, lb_size, UDF_SPACE_ALLOCATED, NULL, NULL);
+ udf_partition->free_unalloc_space -= lb_size;
+ }
+ }
+
+ printf("Free unallocated space on this volume %"PRIu64"\n", udf_partition->free_unalloc_space);
+
+ /* sync unallocated space descriptor (will be updated later?) */
+ udf_sync_space_bitmap(&udf_partition->unalloc_space_queue, unalloc_space_bitmap_descr, lb_size);
+ UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) unalloc_space_bitmap_descr); udf_dump_descriptor((union dscrptr *) unalloc_space_bitmap_descr));
+
+ /* proceed with the other descriptors */
+ /* FIXME: space bitmap recording on disc sector sizes or on lb_sizes? */
+ error = udf_create_empty_unallocated_space_descriptor(sector_size, dscr_ver, serial++, &unallocsp);
+ if (logvol_name) {
+ /* wipe logical volume integrity descriptor sequence and `double check' ? */
+ for (cnt = 0; cnt < integrity_length; cnt++) {
+ udf_write_session_sector(udf_session, integrity_start + cnt, "blank", (uint8_t *) blank, 0, &wrcallback);
+ }
+
+ /* create logical volume */
+ error = udf_create_empty_implementation_use_volume_descriptor(sector_size, dscr_ver, serial++, logvol_name, &implvol);
+ assert(!error);
+ error = udf_create_empty_logical_volume_descriptor(sector_size, dscr_ver, serial++, logvol_name, lb_size, integrity_start, integrity_length, &logvol);
+ assert(!error);
+
+ /* add partition mappings */
+ switch (udf_type) {
+ default :
+ case DISC_TYPE_NORMAL :
+ /* add `normal' physical partition mapping */
+ udf_add_physical_to_logvol(logvol, 1, 0);
+ break;
+ case DISC_TYPE_VIRTUAL :
+ /* XXX virtual on a cd-rw/dvd+rw? XXX */
+ udf_add_physical_to_logvol(logvol, 0, 0);
+ /* udf_add_virtual_to_logvol( logvol, 1, 0); */
+ break;
+ case DISC_TYPE_SPARABLE :
+ /* udf_add_sparable_to_logvol(logvol, 0, 0); */
+ break;
+ case DISC_TYPE_META :
+ printf("No supported format type meta\n");
+ break;
+ }
+ udf_log_vol = NULL;
+ udf_proc_log_vol(udf_pri_vol, &udf_log_vol, logvol);
+ udf_derive_new_logvol_integrity(udf_log_vol);
+ }
+
+ /* and finish the sequence */
+ error = udf_create_empty_terminator_descriptor(sector_size, dscr_ver, &terminator);
+
+ if (logvol_name) {
+ /* allocate space for the filesets descriptor sequence */
+ error = udf_allocate_lbs(udf_log_vol, UDF_C_DSCR, /* length */ 1, "Fileset sequence", &vpart_fsd, &start_lb_fsd, &num_lbs_fsd);
+
+ DEBUG(printf("DEBUG: fsd op lbnum %d, vpart %d\n", start_lb_fsd, vpart_fsd));
+ fsd_loc = &logvol->_lvd_use.fsd_loc;
+ fsd_loc->len = udf_rw32(num_lbs_fsd * lb_size);
+ fsd_loc->loc.lb_num = udf_rw32(start_lb_fsd);
+ fsd_loc->loc.part_num = udf_rw16(vpart_fsd);
+
+ /* create fileset(s) */
+ error = udf_create_empty_fileset_desc(lb_size, dscr_ver, /*filesetnr*/ 0, logvol_name, "fileset", &fileset);
+ assert(fileset);
+ udf_proc_filesetdesc(udf_log_vol, fileset);
+
+ /* create empty root-dir node */
+ udf_mountpoint = SLIST_FIRST(&udf_mountables);
+ udf_init_udf_node(udf_mountpoint, udf_log_vol, "Root", &root_node);
+ udf_allocate_udf_node_on_disc(root_node);
+
+ root_node->stat.st_size = 0;
+ root_node->stat.st_blksize = root_node->udf_log_vol->lb_size;
+ root_node->stat.st_blocks = 0;
+ root_node->stat.st_mode = 0777 | S_IFDIR;
+ root_node->udf_filetype = UDF_ICB_FILETYPE_DIRECTORY;
+ root_node->unique_id = 0; /* UDF 2.3.6.5, 3.2.1.1. */
+
+ root_node->udf_log_vol->num_directories++;
+ udf_insert_node_in_hash(root_node);
+ udf_node_mark_dirty(root_node);
+
+ /* note creation times */
+#ifndef NO_STAT_BIRTHTIME
+ udf_set_timespec_now(&root_node->stat.st_birthtimespec);
+#endif
+ udf_set_timespec_now(&root_node->stat.st_atimespec);
+ udf_set_timespec_now(&root_node->stat.st_ctimespec);
+ udf_set_timespec_now(&root_node->stat.st_mtimespec);
+
+ dscr_entry = TAILQ_FIRST(&root_node->dscr_allocs);
+ fileset->rootdir_icb.loc.lb_num = udf_rw32(dscr_entry->lb_num);
+ fileset->rootdir_icb.loc.part_num = udf_rw16(dscr_entry->vpart_num);
+ fileset->rootdir_icb.len = udf_rw32(lb_size); /* FIXME type 4096? */
+
+ /* set all to writable or we're in trouble here */
+ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN;
+ udf_log_vol->writable = 1;
+ udf_mountpoint->writable = 1;
+
+ /* create `..' directory entry; hardlinks have no stat */
+ error = udf_create_directory_entry(root_node, "..", UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR, root_node, NULL, &dummy_node);
+ assert(error == 0);
+
+ /* adjust reference count for `root' since '..' points to it too but is not considered a link (ECMA 4/14.9.6, 4/8.8.3) */
+ root_node->link_cnt--;
+ }
+
+ /* set all UDF version numbers to one and the same version */
+ if (logvol_name) {
+ /* Implementation use volume descritor's UDF version must be the same as the logical volume's UDF version it describes */
+ /* update/fill in the UDF version chosen */
+ udfver_pos = (uint16_t *) logvol->domain_id.id_suffix;
+ *udfver_pos = udf_rw16(udf_log_vol->min_udf_writever);
+
+ udfver_pos = (uint16_t *) implvol->impl_id.id_suffix;
+ *udfver_pos = udf_rw16(udf_log_vol->min_udf_writever);
+
+ /* FIXME only one fileset now */
+ udfver_pos = (uint16_t *) fileset->domain_id.id_suffix;
+ *udfver_pos = udf_rw16(udf_log_vol->min_udf_writever);
+ }
+
+
+ /* Start to WRITE OUT data VRS and UDF structures */
+ writeout_vds(udf_session, dscr_ver);
+
+ /* start writeout UDF structures */
+#if 0
+ /* wipe space around anchor2 */
+ for (cnt=-20; cnt < 21; cnt++) {
+ udf_write_session_sector(udf_session, anchor2 + cnt, "blank", (uint8_t *) blank, 0, &wrcallback);
+ }
+#endif
+ udf_write_session_descriptor(udf_session, anchor0, "Anchor", (union dscrptr*) anchor, &wrcallback);
+ udf_write_session_descriptor(udf_session, anchor1, "Anchor", (union dscrptr*) anchor, &wrcallback);
+ udf_write_session_descriptor(udf_session, anchor2, "Anchor", (union dscrptr*) anchor, &wrcallback);
+
+ /* writeout volume space */
+ pos = lvd1_area;
+ udf_write_session_descriptor(udf_session, pos++, "Primary", (union dscrptr*) primary, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Partiton", (union dscrptr*) partition, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Unalloc space", (union dscrptr*) unallocsp, &wrcallback);
+ if (logvol_name) {
+ udf_write_session_descriptor(udf_session, pos++, "Logvol", (union dscrptr*) logvol, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Impl. volume", (union dscrptr*) implvol, &wrcallback);
+ }
+ udf_write_session_descriptor(udf_session, pos++, "Terminator", (union dscrptr*) terminator, &wrcallback);
+
+ pos = lvd2_area;
+ udf_write_session_descriptor(udf_session, pos++, "Primary", (union dscrptr*) primary, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Partiton", (union dscrptr*) partition, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Unalloc space", (union dscrptr*) unallocsp, &wrcallback);
+ if (logvol_name) {
+ udf_write_session_descriptor(udf_session, pos++, "Logvol", (union dscrptr*) logvol, &wrcallback);
+ udf_write_session_descriptor(udf_session, pos++, "Impl. volume", (union dscrptr*) implvol, &wrcallback);
+ }
+ udf_write_session_descriptor(udf_session, pos++, "Terminator", (union dscrptr*) terminator, &wrcallback);
+
+ /* the unallocated space bitmap gets written out on sync/dismount */
+ if (logvol_name) {
+ udf_write_logvol_descriptor(udf_log_vol, vpart_fsd, start_lb_fsd, "File set", (union dscrptr*) fileset, &wrcallback);
+ }
+
+ return;
+ }
+
+ /*
+ * If we reach here, we obviously missed a class of recording devices,
+ * better give a error and abort
+ */
+
+ fprintf(stderr, "Internal error: unknown recording class of devices encountered; aborting\n");
+ return;
+}
+
+
+
+int usage(char *program) {
+ fprintf(stderr, "Usage %s [options] devicename\n", program);
+ fprintf(stderr, "Creates a filingsystem on file or a formatted disc\n");
+ fprintf(stderr, "-S volsetname use `volsetname as volume set name'\n"
+ "-P primaryname use `primaryname' as primary volume name\n"
+ "-L volumename use `volumename' as logical volume name (discname)\n"
+ "-v volumenumber when part of a set use this volumenumber\n"
+ "-m volumenumber maximum volumenumber in this set\n"
+ "-2 alter descriptor version number (default 3)\n"
+ "-s numsect create image with number of sectors (file only)\n"
+ "-b blocksize use alternative sectorsize; use only on files/discs\n"
+ "-B mult multiplier for logical sectors (NON-standard!!)\n"
+ "-u level UDF system verbose level\n"
+ "-D debug/verbose SCSI command errors\n"
+ );
+ fprintf(stderr, "use `dd if=/dev/zero bs=64k of=discimage.cd count=...` to create a new discfile. `count` must be >= 19 (about 1.2Mb)\n");
+ fprintf(stderr, "or use the `-b' and `-s' flags to create a new discfile. blocksize needs to be a multiple of 512\n");
+ return 1;
+}
+
+
+int main(int argc, char *argv[]) {
+ struct timeval time_of_day;
+ struct udf_discinfo *disc;
+ char *progname, *volset_name, *privol_name, *logvol_name;
+ uint64_t volset_nr;
+ uint32_t vds_num, max_vol_seq, dscr_ver;
+ uint32_t alt_sector_size, alt_num_sect, lb_mult;
+ off_t fsize;
+ int flag, error, fhandle;
+
+ progname = argv[0];
+ if (argc == 1) return usage(progname);
+
+ volset_name = NULL;
+ privol_name = NULL;
+ logvol_name = NULL;
+ vds_num = 1;
+ max_vol_seq = 1;
+ dscr_ver = 3;
+ alt_sector_size = 0;
+ alt_num_sect = 0;
+ lb_mult = 1;
+
+ while ((flag = getopt(argc, argv, "S:P:L:v:m:2s:b:B:u:D")) != -1) {
+ switch (flag) {
+ case 'S' :
+ volset_name = strdup(optarg);
+ break;
+ case 'P' :
+ privol_name = strdup(optarg);
+ break;
+ case 'L' :
+ logvol_name = strdup(optarg);
+ break;
+ case 'v' :
+ vds_num = atoi(optarg);
+ break;
+ case 'm' :
+ max_vol_seq = atoi(optarg);
+ break;
+ case '2' :
+ dscr_ver = 2;
+ break;
+ case 's' :
+ alt_num_sect = atoi(optarg);
+ break;
+ case 'b' :
+ alt_sector_size = atoi(optarg);
+ break;
+ case 'B' :
+ printf("-B option to set logvol multiplier temporarely disabled\n");
+ /* lb_mult = atoi(optarg); */
+ break;
+ case 'u' :
+ udf_verbose = atoi(optarg);
+ break;
+ case 'D' :
+ uscsilib_verbose = 1;
+ break;
+ default :
+ return usage(progname);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1) return usage(progname);
+
+ srandom(time(NULL));
+ if (!volset_name) {
+ volset_name = malloc(128);
+ (void)gettimeofday(&time_of_day, NULL);
+ volset_nr = (uint64_t) random();
+ volset_nr |= ((uint64_t) time_of_day.tv_sec) << 32;
+ sprintf(volset_name, "%016lx", volset_nr);
+ }
+ if (!privol_name) {
+ privol_name = malloc(32);
+ sprintf(privol_name, "%08lx", random());
+ }
+ if (!logvol_name) {
+ fprintf(stderr, "newfs_udf: no logical volume name passed; not creating logical volume descriptor\nYOU PROLLY DONT WANT THIS\n");
+ }
+ if ((vds_num < 1) || (vds_num > max_vol_seq)) {
+ fprintf(stderr, "Invalid volume seqence number or out of bounds\n");
+ return 1;
+ }
+ if ((dscr_ver < 2) || (dscr_ver > 3)) {
+ fprintf(stderr," UDF upto version 2.50 only supports descriptor versions 2 and 3\n");
+ return 1;
+ }
+
+ /* just one device allowed */
+ SLIST_INIT(&udf_discs_list);
+ printf("Opening device %s\n\n", *argv);
+
+ error = udf_open_disc(*argv, /* discop_flags */ 0, &disc);
+ if (error) {
+ error = 0;
+ if ((alt_num_sect > 0) && (alt_sector_size > 0)) {
+ /* create a file */
+ fprintf(stderr, "Creating new disc image\n");
+ fsize = (off_t) alt_num_sect * alt_sector_size;
+ fhandle = open(*argv, O_CREAT | O_TRUNC | O_RDWR, 0660);
+ if (fhandle) {
+ fsize = ftruncate(fhandle, fsize);
+ if (fsize < 0)
+ error = errno;
+ close(fhandle);
+ } else {
+ error = errno;
+ }
+ }
+ if (!error) {
+ error = udf_open_disc(*argv, /* discop_flags */ 0, &disc);
+ }
+ if (error) {
+ fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error));
+ exit(1);
+ }
+ }
+ SLIST_INSERT_HEAD(&udf_discs_list, disc, next_disc); /* better add it to the disc list */
+
+ /* try to set the alternative sector size */
+ if (alt_sector_size || alt_num_sect) {
+ error = udf_discinfo_alter_perception(disc, alt_sector_size, alt_num_sect);
+ if (error) {
+ exit(0);
+ }
+ }
+
+ udf_unix_init();
+ udf_start_unix_thread();
+
+ printf("\n\n");
+ udf_dump_discinfo(disc);
+
+ /* now do the real thing */
+ newfs_udf(disc, dscr_ver, volset_name, privol_name, logvol_name, vds_num, max_vol_seq, lb_mult, DISC_TYPE_NORMAL);
+
+ printf("Closing disc\n");
+
+ udf_dismount_disc(disc);
+
+ return 0;
+}
+
diff --git a/osta.c b/osta.c
new file mode 100644
index 0000000..109e0ff
--- /dev/null
+++ b/osta.c
@@ -0,0 +1,495 @@
+/* $NetBSD$ */
+
+/*
+ * Various routines from the OSTA 2.01 specs. Copyrights are included with
+ * each code segment. Slight whitespace modifications have been made for
+ * formatting purposes. Typos/bugs have been fixed.
+ *
+ */
+
+#include "osta.h"
+
+
+/*****************************************************************************/
+/***********************************************************************
+ * OSTA compliant Unicode compression, uncompression routines.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * Takes an OSTA CS0 compressed unicode name, and converts
+ * it to Unicode.
+ * The Unicode output will be in the byte order
+ * that the local compiler uses for 16-bit values.
+ * NOTE: This routine only performs error checking on the compID.
+ * It is up to the user to ensure that the unicode buffer is large
+ * enough, and that the compressed unicode name is correct.
+ *
+ * RETURN VALUE
+ *
+ * The number of unicode characters which were uncompressed.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_UncompressUnicode(
+ int numberOfBytes, /* (Input) number of bytes read from media. */
+ byte *UDFCompressed, /* (Input) bytes read from media. */
+ unicode_t *unicode) /* (Output) uncompressed unicode characters. */
+{
+ unsigned int compID;
+ int returnValue, unicodeIndex, byteIndex;
+
+ /* Use UDFCompressed to store current byte being read. */
+ compID = UDFCompressed[0];
+
+ /* First check for valid compID. */
+ if (compID != 8 && compID != 16) {
+ returnValue = -1;
+ } else {
+ unicodeIndex = 0;
+ byteIndex = 1;
+
+ /* Loop through all the bytes. */
+ while (byteIndex < numberOfBytes) {
+ if (compID == 16) {
+ /* Move the first byte to the high bits of the
+ * unicode char.
+ */
+ unicode[unicodeIndex] =
+ UDFCompressed[byteIndex++] << 8;
+ } else {
+ unicode[unicodeIndex] = 0;
+ }
+ if (byteIndex < numberOfBytes) {
+ /*Then the next byte to the low bits. */
+ unicode[unicodeIndex] |=
+ UDFCompressed[byteIndex++];
+ }
+ unicodeIndex++;
+ }
+ returnValue = unicodeIndex;
+ }
+ return(returnValue);
+}
+
+/***********************************************************************
+ * DESCRIPTION:
+ * Takes a string of unicode wide characters and returns an OSTA CS0
+ * compressed unicode string. The unicode MUST be in the byte order of
+ * the compiler in order to obtain correct results. Returns an error
+ * if the compression ID is invalid.
+ *
+ * NOTE: This routine assumes the implementation already knows, by
+ * the local environment, how many bits are appropriate and
+ * therefore does no checking to test if the input characters fit
+ * into that number of bits or not.
+ *
+ * RETURN VALUE
+ *
+ * The total number of bytes in the compressed OSTA CS0 string,
+ * including the compression ID.
+ * A -1 is returned if the compression ID is invalid.
+ */
+int
+udf_CompressUnicode(
+ int numberOfChars, /* (Input) number of unicode characters. */
+ int compID, /* (Input) compression ID to be used. */
+ unicode_t *unicode, /* (Input) unicode characters to compress. */
+ byte *UDFCompressed) /* (Output) compressed string, as bytes. */
+{
+ int byteIndex, unicodeIndex;
+
+ if (compID != 8 && compID != 16) {
+ byteIndex = -1; /* Unsupported compression ID ! */
+ } else {
+ /* Place compression code in first byte. */
+ UDFCompressed[0] = compID;
+
+ byteIndex = 1;
+ unicodeIndex = 0;
+ while (unicodeIndex < numberOfChars) {
+ if (compID == 16) {
+ /* First, place the high bits of the char
+ * into the byte stream.
+ */
+ UDFCompressed[byteIndex++] =
+ (unicode[unicodeIndex] & 0xFF00) >> 8;
+ }
+ /*Then place the low bits into the stream. */
+ UDFCompressed[byteIndex++] =
+ unicode[unicodeIndex] & 0x00FF;
+ unicodeIndex++;
+ }
+ }
+ return(byteIndex);
+}
+
+/*****************************************************************************/
+/*
+ * CRC 010041
+ */
+static unsigned short crc_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+
+uint16_t udf_cksum(uint8_t *s, int n) {
+ uint16_t crc = 0;
+
+ while (n-- > 0)
+ crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8);
+ return crc;
+}
+
+
+/* UNICODE Checksum */
+uint16_t udf_unicode_cksum(uint16_t *s, int n) {
+ uint16_t crc = 0;
+
+ while (n-- > 0) {
+ /* Take high order byte first--corresponds to a big endian
+ * byte stream.
+ */
+ crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8);
+ crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8);
+ }
+ return crc;
+}
+
+/*
+ * Calculates a 16-bit checksum of the Implementation Use
+ * Extended Attribute header or Application Use Extended Attribute
+ * header. The fields AttributeType through ImplementationIdentifier
+ * (or ApplicationIdentifier) inclusively represent the
+ * data covered by the checksum (48 bytes).
+ *
+ */
+uint16_t udf_ea_cksum(uint8_t *data) {
+ uint16_t checksum = 0;
+ int count;
+
+ for (count = 0; count < 48; count++) {
+ checksum += *data++;
+ }
+
+ return checksum;
+}
+
+
+#ifdef MAIN
+unsigned char bytes[] = { 0x70, 0x6A, 0x77 };
+
+main()
+{
+ unsigned short x;
+ x = cksum(bytes, sizeof bytes);
+ printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299);
+ exit(0);
+}
+#endif
+
+/*****************************************************************************/
+#ifdef NEEDS_ISPRINT
+/***********************************************************************
+ * OSTA UDF compliant file name translation routine for OS/2,
+ * Windows 95, Windows NT, Macintosh and UNIX.
+ * Copyright 1995 Micro Design International, Inc.
+ * Written by Jason M. Rinn.
+ * Micro Design International gives permission for the free use of the
+ * following source code.
+ */
+
+/***********************************************************************
+ * To use these routines with different operating systems.
+ *
+ * OS/2
+ * Define OS2
+ * Define MAXLEN = 254
+ *
+ * Windows 95
+ * Define WIN_95
+ * Define MAXLEN = 255
+ *
+ * Windows NT
+ * Define WIN_NT
+ * Define MAXLEN = 255
+ *
+ * Macintosh:
+ * Define MAC.
+ * Define MAXLEN = 31.
+ *
+ * UNIX
+ * Define UNIX.
+ * Define MAXLEN as specified by unix version.
+ */
+
+#define ILLEGAL_CHAR_MARK 0x005F
+#define CRC_MARK 0x0023
+#define EXT_SIZE 5
+#define TRUE 1
+#define FALSE 0
+#define PERIOD 0x002E
+#define SPACE 0x0020
+
+/*** PROTOTYPES ***/
+int IsIllegal(unicode_t ch);
+
+/* Define a function or macro which determines if a Unicode character is
+ * printable under your implementation.
+ */
+
+#include <stdio.h>
+int UnicodeIsPrint(unicode_t ch) {
+ return (ch >=' ') && (ch < 127);
+}
+
+
+int UnicodeLength(unicode_t *string) {
+ int length;
+ length = 0;
+ while (*string++) length++;
+
+ return length;
+}
+
+
+/***********************************************************************
+ * Translates a long file name to one using a MAXLEN and an illegal
+ * char set in accord with the OSTA requirements. Assumes the name has
+ * already been translated to Unicode.
+ *
+ * RETURN VALUE
+ *
+ * Number of unicode characters in translated name.
+ */
+int UDFTransName(
+ unicode_t *newName, /* (Output)Translated name. Must be of length
+ * MAXLEN */
+ unicode_t *udfName, /* (Input) Name from UDF volume.*/
+ int udfLen) /* (Input) Length of UDF Name. */
+{
+ int index, newIndex = 0, needsCRC = FALSE;
+ int extIndex = 0, newExtIndex = 0, hasExt = FALSE;
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ int trailIndex = 0;
+#endif
+ unsigned short valueCRC;
+ unicode_t current;
+ const char hexChar[] = "0123456789ABCDEF";
+
+ for (index = 0; index < udfLen; index++) {
+ current = udfName[index];
+
+ if (IsIllegal(current) || !UnicodeIsPrint(current)) {
+ needsCRC = TRUE;
+ /* Replace Illegal and non-displayable chars with
+ * underscore.
+ */
+ current = ILLEGAL_CHAR_MARK;
+ /* Skip any other illegal or non-displayable
+ * characters.
+ */
+ while(index+1 < udfLen && (IsIllegal(udfName[index+1])
+ || !UnicodeIsPrint(udfName[index+1]))) {
+ index++;
+ }
+ }
+
+ /* Record position of extension, if one is found. */
+ if (current == PERIOD && (udfLen - index -1) <= EXT_SIZE) {
+ if (udfLen == index + 1) {
+ /* A trailing period is NOT an extension. */
+ hasExt = FALSE;
+ } else {
+ hasExt = TRUE;
+ extIndex = index;
+ newExtIndex = newIndex;
+ }
+ }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ /* Record position of last char which is NOT period or space. */
+ else if (current != PERIOD && current != SPACE) {
+ trailIndex = newIndex;
+ }
+#endif
+
+ if (newIndex < MAXLEN) {
+ newName[newIndex++] = current;
+ } else {
+ needsCRC = TRUE;
+ }
+ }
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+ /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */
+ if (trailIndex != newIndex - 1) {
+ newIndex = trailIndex + 1;
+ needsCRC = TRUE;
+ hasExt = FALSE; /* Trailing period does not make an
+ * extension. */
+ }
+#endif
+
+ if (needsCRC) {
+ unicode_t ext[EXT_SIZE];
+ int localExtIndex = 0;
+ if (hasExt) {
+ int maxFilenameLen;
+ /* Translate extension, and store it in ext. */
+ for(index = 0; index<EXT_SIZE &&
+ extIndex + index +1 < udfLen; index++ ) {
+ current = udfName[extIndex + index + 1];
+ if (IsIllegal(current) ||
+ !UnicodeIsPrint(current)) {
+ needsCRC = 1;
+ /* Replace Illegal and non-displayable
+ * chars with underscore.
+ */
+ current = ILLEGAL_CHAR_MARK;
+ /* Skip any other illegal or
+ * non-displayable characters.
+ */
+ while(index + 1 < EXT_SIZE
+ && (IsIllegal(udfName[extIndex +
+ index + 2]) ||
+ !isprint(udfName[extIndex +
+ index + 2]))) {
+ index++;
+ }
+ }
+ ext[localExtIndex++] = current;
+ }
+
+ /* Truncate filename to leave room for extension and
+ * CRC.
+ */
+ maxFilenameLen = ((MAXLEN - 5) - localExtIndex - 1);
+ if (newIndex > maxFilenameLen) {
+ newIndex = maxFilenameLen;
+ } else {
+ newIndex = newExtIndex;
+ }
+ } else if (newIndex > MAXLEN - 5) {
+ /*If no extension, make sure to leave room for CRC. */
+ newIndex = MAXLEN - 5;
+ }
+ newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */
+
+ /*Calculate CRC from original filename from FileIdentifier. */
+ valueCRC = udf_unicode_cksum(udfName, udfLen);
+ /* Convert 16-bits of CRC to hex characters. */
+ newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
+ newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
+ newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
+ newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
+
+ /* Place a translated extension at end, if found. */
+ if (hasExt) {
+ newName[newIndex++] = PERIOD;
+ for (index = 0;index < localExtIndex ;index++ ) {
+ newName[newIndex++] = ext[index];
+ }
+ }
+ }
+ return(newIndex);
+}
+
+#if defined OS2 || defined WIN_95 || defined WIN_NT
+/***********************************************************************
+ * Decides if a Unicode character matches one of a list
+ * of ASCII characters.
+ * Used by OS2 version of IsIllegal for readability, since all of the
+ * illegal characters above 0x0020 are in the ASCII subset of Unicode.
+ * Works very similarly to the standard C function strchr().
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if the Unicode character is in the given ASCII string.
+ */
+int UnicodeInString(
+ unsigned char *string, /* (Input) String to search through. */
+ unicode_t ch) /* (Input) Unicode char to search for. */
+{
+ int found = FALSE;
+ while (*string != '\0' && found == FALSE) {
+ /* These types should compare, since both are unsigned
+ * numbers. */
+ if (*string == ch) {
+ found = TRUE;
+ }
+ string++;
+ }
+ return(found);
+}
+#endif /* OS2 */
+
+/***********************************************************************
+ * Decides whether the given character is illegal for a given OS.
+ *
+ * RETURN VALUE
+ *
+ * Non-zero if char is illegal.
+ */
+int IsIllegal(unicode_t ch)
+{
+#ifdef MAC
+ /* Only illegal character on the MAC is the colon. */
+ if (ch == 0x003A) {
+ return(1);
+ } else {
+ return(0);
+ }
+
+#elif defined UNIX
+ /* Illegal UNIX characters are NULL and slash. */
+ if (ch == 0x0000 || ch == 0x002F) {
+ return(1);
+ } else {
+ return(0);
+ }
+
+#elif defined OS2 || defined WIN_95 || defined WIN_NT
+ /* Illegal char's for OS/2 according to WARP toolkit. */
+ if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) {
+ return(1);
+ } else {
+ return(0);
+ }
+#endif
+}
+#endif
diff --git a/osta.h b/osta.h
new file mode 100644
index 0000000..af6918b
--- /dev/null
+++ b/osta.h
@@ -0,0 +1,45 @@
+/* $NetBSD$ */
+
+/*
+ * Prototypes for the OSTA functions
+ *
+ */
+
+
+#ifndef _OSTA_H_
+#define _OSTA_H_
+
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifndef UNIX
+#define UNIX
+#endif
+
+#ifndef MAXLEN
+#define MAXLEN 255
+#endif
+
+
+/***********************************************************************
+ * The following two typedef's are to remove compiler dependancies.
+ * byte needs to be unsigned 8-bit, and unicode_t needs to be
+ * unsigned 16-bit.
+ */
+typedef uint16_t unicode_t;
+typedef uint8_t byte;
+
+
+int udf_UncompressUnicode(int, byte *, unicode_t *);
+int udf_CompressUnicode(int, int, unicode_t *, byte *);
+uint16_t udf_cksum(uint8_t *s, int n);
+uint16_t udf_unicode_cksum(uint16_t *s, int n);
+uint16_t udf_ea_cksum(uint8_t *);
+int UDFTransName(unicode_t *, unicode_t *, int);
+int UnicodeLength(unicode_t *string);
+
+
+#endif /* _OSTA_H_ */
+
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000..823dc97
--- /dev/null
+++ b/queue.h
@@ -0,0 +1,604 @@
+/* $NetBSD: queue.h,v 1.35 2004/02/26 01:20:47 wiz Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so only elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#if defined(_KERNEL) && defined(QUEUEDEBUG)
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
+ if ((head)->lh_first && \
+ (head)->lh_first->field.le_prev != &(head)->lh_first) \
+ panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_OP(elm, field) \
+ if ((elm)->field.le_next && \
+ (elm)->field.le_next->field.le_prev != \
+ &(elm)->field.le_next) \
+ panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
+ (elm)->field.le_next = (void *)1L; \
+ (elm)->field.le_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_LIST_OP(elm, field)
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ QUEUEDEBUG_LIST_OP((elm), field) \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var); \
+ (var) = ((var)->field.le_next))
+
+/*
+ * List access methods.
+ */
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var); \
+ (var) = ((var)->field.sqe_next))
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if defined(_KERNEL) && defined(QUEUEDEBUG)
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&head, (void *)&head }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != (void *)(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != (void *)(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/udf.c b/udf.c
new file mode 100644
index 0000000..bdb539a
--- /dev/null
+++ b/udf.c
@@ -0,0 +1,6476 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf.c" is part of the UDFclient toolkit.
+ * File $Id: udf.c,v 1.294 2015/08/05 18:26:29 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "udf.h"
+#include "udf_bswap.h"
+#include "udf_discop.h"
+#include "udf_unix.h"
+#include "uio.h"
+#include "dirhash.h"
+#include <pthread.h>
+
+
+/* for scsilib */
+const char *dvname="UDF device";
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/* #define DEBUG(a) { a; } */
+#define DEBUG(a) if (0) { a; }
+
+
+#if 1
+ extern void udf_dump_descriptor(union dscrptr *dscrpt);
+ extern void udf_dump_file_entry(struct file_entry *fe);
+ extern void udf_dump_extfile_entry(struct extfile_entry *efe);
+ extern void udf_dump_alloc_extent(struct alloc_ext_entry *ext, int addr_type);
+ extern void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping);
+ extern void udf_dump_disc_anchors(struct udf_discinfo *disc);
+ extern void udf_dump_alive_sets(void);
+ extern void udf_dump_root_dir(struct udf_mountpoint *mountpoint);
+ extern void udf_dump_timestamp(char *dscr, struct timestamp *t);
+#else
+ void udf_dump_descriptor(union dscrptr *dscrpt) {};
+ extern void udf_dump_file_entry(struct file_entry *fe) {};
+ extern void udf_dump_extfile_entry(struct extfile_entry *efe) {};
+ extern void udf_dump_alloc_extent(struct alloc_ext_entry *ext, int addr_type) {};
+ void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping) {};
+ void udf_dump_disc_anchors(struct udf_discinfo *disc) {};
+ void udf_dump_alive_sets(void) {};
+ void udf_dump_root_dir(struct udf_mountpoint *mountpoint) {};
+ void udf_dump_timestamp(char *dscr, struct timestamp *t) {};
+#endif
+
+
+/* global settings outside udf.h */
+int udf_verbose = UDF_VERBLEV_ACTIONS;
+
+#define UDF_INODE_NUM_GUESS 2048
+
+
+/* predefines */
+int udf_validate_tag_and_crc_sums(union dscrptr *dscr);
+void udf_node_mark_dirty(struct udf_node *udf_node);
+
+static void udf_set_imp_id(struct regid *regid);
+static void udf_set_app_id(struct regid *regid);
+static void udf_node_unmark_dirty(struct udf_node *udf_node);
+static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num);
+static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype);
+static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname);
+static int udf_remove_directory_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname);
+
+
+/******************************************************************************************
+ *
+ * Filename space conversion
+ *
+ ******************************************************************************************/
+
+void udf_to_unix_name(char *result, char *id, int len, struct charspec *chsp) {
+ uint16_t raw_name[1024], unix_name[1024];
+ uint16_t *inchp, ch;
+ uint8_t *outchp;
+ int ucode_chars, nice_uchars;
+
+ assert(sizeof(char) == sizeof(uint8_t));
+ outchp = (uint8_t *) result;
+ if ((chsp->type == 0) && (strcmp((char*) chsp->inf, "OSTA Compressed Unicode") == 0)) {
+ *raw_name = *unix_name = 0;
+ ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name);
+ ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name));
+ nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars);
+ for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) {
+ ch = *inchp;
+ /* sloppy unicode -> latin */
+ *outchp++ = ch & 255;
+ if (!ch) break;
+ }
+ *outchp++ = 0;
+ } else {
+ /* assume 8bit char length byte latin-1 */
+ assert(*id == 8);
+ strncpy((char *) result, (char *) (id+1), strlen((char *) (id+1)));
+ }
+}
+
+
+void unix_to_udf_name(char *result, char *name, uint8_t *result_len, struct charspec *chsp) {
+ uint16_t raw_name[1024];
+ int udf_chars, name_len;
+ char *inchp;
+ uint16_t *outchp;
+
+ /* convert latin-1 or whatever to unicode-16 */
+ *raw_name = 0;
+ name_len = 0;
+ inchp = name;
+ outchp = raw_name;
+ while (*inchp) {
+ *outchp++ = (uint16_t) (*inchp++);
+ name_len++;
+ }
+
+ if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) {
+ udf_chars = udf_CompressUnicode(name_len, 8, (unicode_t *) raw_name, (byte *) result);
+ } else {
+ /* assume 8bit char length byte latin-1 */
+ *result++ = 8; udf_chars = 1;
+ strncpy(result, name + 1, strlen(name+1));
+ udf_chars += strlen(name);
+ }
+ *result_len = udf_chars;
+}
+
+
+static char *udf_get_compound_name(struct udf_mountpoint *mountpoint) {
+ static char compound[128+128+32+32+1];
+ struct charspec *charspec;
+ struct udf_log_vol *udf_log_vol;
+ struct udf_pri_vol *udf_pri_vol;
+ char *unix_name;
+
+ udf_log_vol = mountpoint->udf_log_vol;
+ udf_pri_vol = udf_log_vol->primary;
+
+ charspec = &udf_pri_vol->pri_vol->desc_charset;
+ assert(charspec->type == 0);
+ assert(strcmp((const char *) charspec->inf, "OSTA Compressed Unicode")==0);
+
+ unix_name = compound;
+
+ udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->volset_id, 128, charspec);
+ strcat(unix_name, ":");
+ unix_name += strlen(unix_name);
+
+ udf_to_unix_name(unix_name, udf_pri_vol->pri_vol->vol_id, 32, charspec);
+ strcat(unix_name, ":");
+ unix_name += strlen(unix_name);
+
+ udf_to_unix_name(unix_name, udf_log_vol->log_vol->logvol_id, 128, charspec);
+ strcat(unix_name, ":");
+ unix_name += strlen(unix_name);
+
+ udf_to_unix_name(unix_name, mountpoint->fileset_desc->fileset_id, 32, charspec);
+
+ return compound;
+}
+
+
+/******************************************************************************************
+ *
+ * Dump helpers for printing out information during parse
+ *
+ ******************************************************************************************/
+
+void udf_dump_long_ad(char *prefix, struct long_ad *adr) {
+ printf("%s at sector %d within partion space %d for %d bytes\n", prefix,
+ udf_rw32(adr->loc.lb_num), udf_rw16(adr->loc.part_num),
+ udf_rw32(adr->len)
+ );
+}
+
+
+void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp) {
+ uint16_t raw_name[1024];
+ uint16_t *pos, ch;
+ int ucode_chars;
+
+ if (prefix) printf("%s ", prefix);
+ if ((chsp->type == 0) && (strcmp((char *) chsp->inf, "OSTA Compressed Unicode") == 0)) {
+ /* print the identifier using the OSTA compressed unicode */
+ printf("`");
+ ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name);
+ for (pos = raw_name; ucode_chars > 0; pos++, ucode_chars--) {
+ ch = *pos; /* OSTA code decompresses to machine endian */
+ if (!ch) break;
+ if ((ch < 32) || (ch > 255)) {
+ printf("[%d]", ch);
+ } else {
+ printf("%c", ch & 255);
+ }
+ }
+ printf("`");
+ } else {
+ printf("(roughly) `%s`", id+1);
+ }
+ if (prefix) printf("\n");
+}
+
+
+void udf_dump_volume_name(char *prefix, struct udf_log_vol *udf_log_vol) {
+ if (prefix) printf("%s%s", prefix, udf_log_vol->primary->udf_session->session_offset?" (local) ":" ");
+ udf_dump_id(NULL, 128, udf_log_vol->primary->pri_vol->volset_id, &udf_log_vol->primary->pri_vol->desc_charset);
+ printf(":");
+ udf_dump_id(NULL, 32, udf_log_vol->primary->pri_vol->vol_id, &udf_log_vol->primary->pri_vol->desc_charset);
+ printf(":");
+ udf_dump_id(NULL, 128, udf_log_vol->log_vol->logvol_id, &udf_log_vol->log_vol->desc_charset);
+ if (prefix) printf("\n");
+}
+
+
+/******************************************************************************************
+ *
+ * UDF tag checkers and size calculator
+ *
+ ******************************************************************************************/
+
+
+/* not used extensively enough yet */
+int udf_check_tag(union dscrptr *dscr) {
+ struct desc_tag *tag = &dscr->tag;
+ uint8_t *pos, sum, cnt;
+
+ /* check TAG header checksum */
+ pos = (uint8_t *) tag;
+ sum = 0;
+
+ for(cnt = 0; cnt < 16; cnt++) {
+ if (cnt != 4) sum += *pos;
+ pos++;
+ }
+ if (sum != tag->cksum) {
+ /* bad tag header checksum; this is not a valid tag */
+ DEBUG(printf("Bad checksum\n"));
+ return EINVAL;
+ }
+ return 0;
+}
+
+
+int udf_check_tag_payload(union dscrptr *dscr) {
+ struct desc_tag *tag = &dscr->tag;
+ uint16_t crc;
+
+ /* check payload CRC if applicable */
+ if (udf_rw16(tag->desc_crc_len) == 0) return 0;
+
+ crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len));
+ if (crc != udf_rw16(tag->desc_crc)) {
+ DEBUG(printf("ERROR: CRC bad read 0x%0x calc 0x%0x\n", udf_rw16(tag->desc_crc), crc));
+ /* bad payload CRC; this is a broken tag */
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+
+int udf_validate_tag_sum(union dscrptr *dscr) {
+ struct desc_tag *tag = &dscr->tag;
+ uint8_t *pos, sum, cnt;
+
+ /* calculate TAG header checksum */
+ pos = (uint8_t *) tag;
+ sum = 0;
+
+ for(cnt = 0; cnt < 16; cnt++) {
+ if (cnt != 4) sum += *pos;
+ pos++;
+ }
+ tag->cksum = sum; /* 8 bit */
+
+ return 0;
+}
+
+
+/* assumes sector number of descriptor to be allready present */
+int udf_validate_tag_and_crc_sums(union dscrptr *dscr) {
+ struct desc_tag *tag = &dscr->tag;
+ uint16_t crc;
+
+ /* check payload CRC if applicable */
+ if (udf_rw16(tag->desc_crc_len) > 0) {
+ crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, udf_rw16(tag->desc_crc_len));
+ tag->desc_crc = udf_rw16(crc);
+ }
+
+ /* calculate TAG header checksum */
+ return udf_validate_tag_sum(dscr);
+}
+
+
+int udf_check_tag_presence(union dscrptr *dscr, int TAG) {
+ struct desc_tag *tag = &dscr->tag;
+ int error;
+
+ error = udf_check_tag(dscr);
+ if (error) return error;
+
+ if (udf_rw16(tag->id) != TAG) {
+ DEBUG(fprintf(stderr, "looking for tag %d but found %d\n", TAG, udf_rw16(tag->id)));
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * for malloc() purposes ... rather have an upperlimit than an exact size
+ */
+uint64_t udf_calc_tag_malloc_size(union dscrptr *dscr, uint32_t udf_sector_size) {
+ uint32_t size, tag_id;
+
+ tag_id = udf_rw16(dscr->tag.id);
+
+ switch (tag_id) {
+ case TAGID_LOGVOL :
+ size = sizeof(struct logvol_desc) - 1; /* maps[1] */
+ size += udf_rw32(dscr->lvd.mt_l);
+ break;
+ case TAGID_UNALLOC_SPACE :
+ size = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); /* alloc_desc[1] */
+ size += udf_rw32(dscr->usd.alloc_desc_num) * sizeof(struct extent_ad);
+ break;
+ case TAGID_FID :
+ size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu);
+ size = (size + 3) & ~3;
+ return size; /* RETURN !! */
+ case TAGID_LOGVOL_INTEGRITY :
+ size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* tables[1] */
+ size += udf_rw32(dscr->lvid.l_iu);
+ size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t));
+ break;
+ case TAGID_SPACE_BITMAP :
+ size = sizeof(struct space_bitmap_desc) - 1; /* data[1] */
+ size += udf_rw32(dscr->sbd.num_bytes);
+ break;
+ case TAGID_SPARING_TABLE :
+ size = sizeof(struct udf_sparing_table) - sizeof(struct spare_map_entry); /* entries[1] */
+ size += udf_rw16(dscr->spt.rt_l) * sizeof(struct spare_map_entry);
+ break;
+ case TAGID_FENTRY :
+ size = sizeof(struct file_entry);
+ size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; /* data[0] */
+ break;
+ case TAGID_EXTFENTRY :
+ size = sizeof(struct extfile_entry);
+ size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; /* data[0] */
+ break;
+ case TAGID_FSD :
+ size = sizeof(struct fileset_desc);
+ break;
+ default :
+ size = sizeof(union dscrptr);
+ break;
+ }
+
+ if ((size == 0) || (udf_sector_size == 0)) return 0;
+ return ((size + udf_sector_size -1) / udf_sector_size) * udf_sector_size;
+}
+
+
+/* explicit only for FID's */
+static int
+udf_fidsize(struct fileid_desc *fid)
+{
+ int size;
+
+ size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu);
+ size = (size + 3) & ~3;
+
+ return size;
+}
+
+
+/******************************************************************************************
+ *
+ * Logical to physical adres transformation
+ *
+ ******************************************************************************************/
+
+
+/* convert (udf_log_vol, vpart_num) to a udf_partion structure */
+int udf_logvol_vpart_to_partition(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_part_mapping **udf_part_mapping_ptr, struct udf_partition **udf_partition_ptr) {
+ struct udf_volumeset *udf_volumeset;
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ uint32_t part_num;
+ int found;
+
+ assert(udf_log_vol);
+ assert(!SLIST_EMPTY(&udf_log_vol->part_mappings));
+
+ /* clear result */
+ if (udf_part_mapping_ptr) *udf_part_mapping_ptr = NULL;
+ if (udf_partition_ptr) *udf_partition_ptr = NULL;
+
+ /* map the requested partition map to the physical udf partition */
+ found = 0;
+ SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ if (udf_part_mapping->udf_virt_part_num == vpart_num) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printf("\t\t\tVirtual partition number %d not found!\n", vpart_num);
+ return EINVAL;
+ }
+
+ assert(udf_part_mapping); /* call me paranoid */
+ part_num = udf_part_mapping->udf_phys_part_num;
+
+ /* search for the physical partition information */
+ udf_volumeset = udf_log_vol->primary->volumeset;
+ SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) {
+ if (udf_rw16(udf_partition->partition->part_num) == part_num) break;
+ }
+
+ if (!udf_partition) {
+ printf("\t\t\tNo information known about partition %d yet!\n", part_num);
+ printf("\t\t\t\tPlease insert volume %d of this volumeset and try again\n", udf_part_mapping->vol_seq_num);
+ return ENOENT;
+ }
+
+ if (udf_part_mapping_ptr) *udf_part_mapping_ptr = udf_part_mapping;
+ if (udf_partition_ptr) *udf_partition_ptr = udf_partition;
+ return 0;
+}
+
+
+/* no recursive translations yet (UDF OK) */
+/* All translation is done in 64 bits to prevent bitrot and returns the PARTITION relative address */
+int udf_vpartoff_to_sessionoff(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *udf_part_mapping, struct udf_partition *udf_partition, uint64_t offset, uint64_t *ses_off, uint64_t *trans_valid_len) {
+ struct spare_map_entry *sp_entry;
+ struct udf_node *udf_node;
+ struct udf_allocentry *alloc_entry;
+
+ uint64_t part_start, part_length;
+ uint64_t eff_sector, eff_offset;
+ uint64_t trans_sector;
+ uint64_t cur_offset;
+ uint32_t len, lb_num, block_offset;
+ uint32_t entry, entries;
+ uint32_t sector_size, lb_size;
+
+ uint64_t packet_num, packet_rlb;
+ uint64_t packet_len;
+
+ uint32_t vat_entries, *vat_pos;
+ int flags;
+
+ assert(udf_part_mapping);
+ assert(udf_partition);
+ assert(ses_off);
+ assert(trans_valid_len);
+
+ /* not ok, but rather this than a dangling random value */
+ *ses_off = UINT_MAX;
+ *trans_valid_len = 0;
+
+ lb_size = udf_log_vol->lb_size;
+ sector_size = udf_log_vol->sector_size;
+ part_start = (uint64_t) udf_rw32(udf_partition->partition->start_loc) * sector_size;
+ part_length = (uint64_t) udf_rw32(udf_partition->partition->part_len) * sector_size;
+
+ /* get the offset (in bytes) in the partition and check its validity */
+ if (offset >= part_length) {
+ printf("\t\toffset %"PRIu64" is outside partition %d!\n", offset, udf_rw16(udf_partition->partition->part_num));
+ return EFAULT;
+ }
+
+ /* do the address translations based on the partition mapping type */
+ /* translation of virt/sparable etc. is assumed to be done in logical block sizes */
+ switch (udf_part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_PHYSICAL :
+ /* nothing to be done; physical is logical */
+ *ses_off = part_start + offset; /* 1:1 */
+ *trans_valid_len = part_length - offset; /* rest of partition */
+ return 0;
+
+ case UDF_PART_MAPPING_VIRTUAL :
+ vat_entries = udf_part_mapping->vat_entries;
+ vat_pos = (uint32_t *) udf_part_mapping->vat_translation;
+
+ /* this translation is dependent on logical sector numbers */
+ eff_sector = offset / lb_size;
+ eff_offset = offset % lb_size;
+
+ /* TODO check range for logical sector against VAT length */
+ trans_sector = vat_pos[eff_sector];
+ *ses_off = part_start + (trans_sector * lb_size) + eff_offset; /* trans sectors are in lb->lb ? */
+ *trans_valid_len = lb_size - eff_offset; /* maximum one logical sector */
+ return 0;
+
+ case UDF_PART_MAPPING_SPARABLE :
+ /* this translation is dependent on logical sector numbers */
+ *ses_off = part_start + offset; /* 1:1 */
+ eff_sector = offset / lb_size;
+ eff_offset = offset % lb_size;
+
+ /* transform on packet-length base */
+ packet_len = udf_rw16(udf_part_mapping->udf_pmap->pms.packet_len); /* in lb */
+ entries = udf_rw16(udf_part_mapping->sparing_table->rt_l);
+
+ packet_num = (eff_sector / packet_len) * packet_len;
+ packet_rlb = eff_sector % packet_len; /* within packet */
+
+ /* translate this packet; source is in partition, destination is absolute disc address */
+ sp_entry = &udf_part_mapping->sparing_table->entries[0];
+ for (entry = 0; entry < entries; entry++) {
+ if (udf_rw32(sp_entry->org) - packet_num == 0) {
+ /* mappings contain absolute disc addresses, so no partition offsets please */
+ *ses_off = (uint64_t) (udf_rw32(sp_entry->map) + packet_rlb) * lb_size + eff_offset;
+ break;
+ }
+ sp_entry++;
+ }
+ *trans_valid_len = (packet_len - packet_rlb) * lb_size; /* maximum one packet */
+ return 0;
+
+ case UDF_PART_MAPPING_META :
+ /* We follow the allocation entries to calculate our offset */
+ udf_node = udf_part_mapping->meta_file;
+ assert(udf_node->addr_type != UDF_ICB_INTERN_ALLOC);
+
+ /* find sector in the allocation space */
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ cur_offset = 0;
+ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) {
+ len = alloc_entry->len;
+ lb_num = alloc_entry->lb_num;
+ /* vpart_num = alloc_entry->vpart_num; */
+ flags = alloc_entry->flags;
+
+ /* check overlap with this alloc entry */
+ if (cur_offset + len > offset) {
+ assert(((offset - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */
+ if (flags != UDF_EXT_ALLOCATED)
+ break;
+ block_offset = offset - cur_offset;
+ *ses_off = part_start + lb_num * lb_size + block_offset; /* 1:1 within the block */
+ *trans_valid_len = len - block_offset; /* rest of this chunk */
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ return 0;
+ }
+ cur_offset += len;
+ } /* FOREACH */
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ printf("\t\toffset %"PRIu64" is not translated within current metadata partition %d file descriptor!\n", offset, udf_rw16(udf_partition->partition->part_num));
+ return EFAULT;
+ case UDF_PART_MAPPING_ERROR :
+ default :
+ break;
+ }
+ printf("Unsupported or bad mapping %d; can't translate\n", udf_part_mapping->udf_part_mapping_type);
+
+ return EFAULT;
+}
+
+
+
+/******************************************************************************************
+ *
+ * udf_node creator, destructor and syncer
+ *
+ ******************************************************************************************/
+
+
+static mode_t udf_perm_to_unix_mode(uint32_t perm) {
+ mode_t mode;
+
+ mode = ((perm & UDF_FENTRY_PERM_USER_MASK) );
+ mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK ) >> 2);
+ mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
+
+ return mode;
+}
+
+
+static uint32_t unix_mode_to_udf_perm(mode_t mode) {
+ uint32_t perm;
+
+ perm = ((mode & S_IRWXO) );
+ perm |= ((mode & S_IRWXG) << 2);
+ perm |= ((mode & S_IRWXU) << 4);
+ perm |= ((mode & S_IWOTH) << 3);
+ perm |= ((mode & S_IWGRP) << 5);
+ perm |= ((mode & S_IWUSR) << 7);
+
+ return perm;
+}
+
+
+/*
+ * Fill in timestamp structure based on clock_gettime(). Time is reported back as a time_t
+ * accompanied with a nano second field.
+ *
+ * The husec, usec and csec could be relaxed in type.
+ */
+static void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) {
+ struct tm tm;
+ uint64_t husec, usec, csec;
+
+ bzero(timestamp, sizeof(struct timestamp));
+ gmtime_r(&timespec->tv_sec, &tm);
+
+ /*
+ * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1.
+ *
+ * Lower 12 bits are two complement signed timezone offset if bit 12
+ * (method 1) is clear. Otherwise if bit 12 is set, specify timezone
+ * offset to -2047 i.e. unsigned `zero'
+ */
+
+ timestamp->type_tz = udf_rw16((1<<12) + 0); /* has to be method 1 for CUT/GMT */
+ timestamp->year = udf_rw16(tm.tm_year + 1900);
+ timestamp->month = tm.tm_mon + 1; /* `tm' structure uses 0..11 for months */
+ timestamp->day = tm.tm_mday;
+ timestamp->hour = tm.tm_hour;
+ timestamp->minute = tm.tm_min;
+ timestamp->second = tm.tm_sec;
+
+ usec = (timespec->tv_nsec + 500) / 1000; /* round (if possible) */
+ husec = usec / 100;
+ usec -= husec * 100; /* we only want 0-99 in usec */
+ csec = husec / 100; /* we get 0-99 in csec */
+ husec -= csec * 100; /* we only want 0-99 in husec */
+
+ timestamp->centisec = csec;
+ timestamp->hund_usec = husec;
+ timestamp->usec = usec;
+}
+
+
+void udf_set_timestamp_now(struct timestamp *timestamp) {
+ struct timespec now;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ udf_timespec_to_timestamp(&now, timestamp);
+}
+
+
+/* implemented as a seperate function to allow tuning */
+void udf_set_timespec_now(struct timespec *timespec) {
+ clock_gettime(CLOCK_REALTIME, timespec);
+}
+
+
+int udf_insanetimespec(struct timespec *check) {
+ struct timespec now;
+ struct tm tm;
+
+ gmtime_r(&check->tv_sec, &tm);
+
+ /* since our converters can only deal with timestamps after 1970 we
+ * reject earlier */
+ if (tm.tm_year < 1970) return 1;
+
+ /* don't accept values from the future; FFS or NFS might not mind, but
+ * UDF does! */
+ clock_gettime(CLOCK_REALTIME, &now);
+ if (now.tv_sec < check->tv_sec)
+ return 1;
+ if ((now.tv_sec == check->tv_sec) && (now.tv_nsec < check->tv_nsec))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Timestamp to timespec conversion code is taken with small modifications
+ * from FreeBSD /sys/fs/udf by Scott Long <scottl@freebsd.org>
+ */
+
+static int mon_lens[2][12] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+
+static int
+udf_isaleapyear(int year)
+{
+ int i;
+
+ i = (year % 4) ? 0 : 1;
+ i &= (year % 100) ? 1 : 0;
+ i |= (year % 400) ? 0 : 1;
+
+ return i;
+}
+
+
+static void udf_timestamp_to_timespec(struct timestamp *timestamp, struct timespec *timespec) {
+ uint32_t usecs, secs, nsecs;
+ uint16_t tz;
+ int i, lpyear, daysinyear, year;
+
+ timespec->tv_sec = secs = 0;
+ timespec->tv_nsec = nsecs = 0;
+
+ /*
+ * DirectCD seems to like using bogus year values.
+ * Distrust time->month especially, since it will be used for an array
+ * index.
+ */
+ year = udf_rw16(timestamp->year);
+ if ((year < 1970) || (timestamp->month > 12)) {
+ return;
+ }
+
+ /* Calculate the time and day */
+ usecs = timestamp->usec + 100*timestamp->hund_usec + 10000*timestamp->centisec;
+ nsecs = usecs * 1000;
+ secs = timestamp->second;
+ secs += timestamp->minute * 60;
+ secs += timestamp->hour * 3600;
+ secs += (timestamp->day-1) * 3600 * 24; /* day : 1-31 */
+
+ /* Calclulate the month */
+ lpyear = udf_isaleapyear(year);
+ for (i = 1; i < timestamp->month; i++)
+ secs += mon_lens[lpyear][i-1] * 3600 * 24; /* month: 1-12 */
+
+ for (i = 1970; i < year; i++) {
+ daysinyear = udf_isaleapyear(i) + 365 ;
+ secs += daysinyear * 3600 * 24;
+ }
+
+ /*
+ * Calculate the time zone. The timezone is 12 bit signed 2's
+ * compliment, so we gotta do some extra magic to handle it right.
+ */
+ tz = udf_rw16(timestamp->type_tz);
+ tz &= 0x0fff; /* only lower 12 bits are significant */
+ if (tz & 0x0800) /* sign extention */
+ tz |= 0xf000;
+
+ /* TODO check timezone conversion */
+#if 1
+ /* check if we are specified a timezone to convert */
+ if (udf_rw16(timestamp->type_tz) & 0x1000)
+ if ((int16_t) tz != -2047)
+ secs -= (int16_t) tz * 60;
+#endif
+ timespec->tv_sec = secs;
+ timespec->tv_nsec = nsecs;
+}
+
+
+static void udf_node_get_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) {
+ struct stat *stat;
+ struct file_entry *file_entry;
+ struct extfile_entry *extfile_entry;
+ struct timestamp *atime, *mtime, *ctime, *attrtime;
+ uint64_t inf_len, unique_id;
+ uint32_t uid, gid, udf_perm;
+ uint16_t fe_tag;
+ uint16_t udf_icbtag_flags, serial_num, link_cnt;
+ uint8_t filetype;
+
+ assert(udf_node);
+ assert(dscrptr);
+ stat = &udf_node->stat;
+
+ /* check if its an normal file entry or a extended file entry ICB */
+ fe_tag = udf_rw16(dscrptr->tag.id);
+ if (fe_tag == TAGID_FENTRY) {
+ file_entry = &dscrptr->fe;
+#if 0
+ prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs);
+ strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param));
+ entries = udf_rw16(file_entry->icbtag.max_num_entries);
+ strategy = udf_rw16(file_entry->icbtag.strat_type);
+ data_length = udf_rw32(file_entry->l_ad);
+ addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea);
+#endif
+ filetype = file_entry->icbtag.file_type;
+ inf_len = udf_rw64(file_entry->inf_len);
+ uid = udf_rw32(file_entry->uid);
+ gid = udf_rw32(file_entry->gid);
+ udf_perm = udf_rw32(file_entry->perm);
+ serial_num = udf_rw16(file_entry->tag.serial_num);
+ udf_icbtag_flags = udf_rw16(file_entry->icbtag.flags);
+ link_cnt = udf_rw16(file_entry->link_cnt);
+ unique_id = udf_rw64(file_entry->unique_id);
+ atime = &file_entry->atime;
+ mtime = &file_entry->mtime;
+ ctime = &file_entry->mtime; /* XXX assumption */
+ attrtime = &file_entry->attrtime;
+ } else if (fe_tag == TAGID_EXTFENTRY) {
+ extfile_entry = &dscrptr->efe;
+#if 0
+ prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs);
+ strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param));
+ entries = udf_rw16(extfile_entry->icbtag.max_num_entries);
+ strategy = udf_rw16(extfile_entry->icbtag.strat_type);
+ data_length = udf_rw32(extfile_entry->l_ad);
+ addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea);
+#endif
+ filetype = extfile_entry->icbtag.file_type;
+ inf_len = udf_rw64(extfile_entry->inf_len);
+ uid = udf_rw32(extfile_entry->uid);
+ gid = udf_rw32(extfile_entry->gid);
+ udf_perm = udf_rw32(extfile_entry->perm);
+ serial_num = udf_rw16(extfile_entry->tag.serial_num);
+ udf_icbtag_flags = udf_rw16(extfile_entry->icbtag.flags);
+ link_cnt = udf_rw16(extfile_entry->link_cnt); /* how many FID's are linked to this (ext)fentry */
+ unique_id = udf_rw64(extfile_entry->unique_id); /* unique file ID */
+ atime = &extfile_entry->atime;
+ mtime = &extfile_entry->mtime;
+ ctime = &extfile_entry->ctime;
+ attrtime = &extfile_entry->attrtime;
+ } else {
+ printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag);
+ udf_dump_descriptor(dscrptr);
+ return;
+ }
+
+ /* fill in (parts of) the stat structure */
+ /* XXX important info missing still like access mode, times etc. XXX */
+ udf_node->udf_filetype = filetype;
+ udf_node->serial_num = serial_num;
+ udf_node->udf_icbtag_flags = udf_icbtag_flags;
+ udf_node->link_cnt = link_cnt; /* how many FID's are linked to this (ext)fentry */
+ udf_node->unique_id = unique_id; /* unique file ID */
+
+ /* fill in stat basics */
+ bzero(stat, sizeof(struct stat));
+ stat->st_ino = unique_id; /* lowest 32 bit(!) only */
+ stat->st_mode = udf_perm_to_unix_mode(udf_perm); /* CONVERT from udf_perm */
+ stat->st_mode |= (udf_translate_icb_filetype_to_dirent_filetype(filetype) & DT_DIR) ? S_IFDIR : S_IFREG;
+ stat->st_uid = uid;
+ stat->st_gid = gid;
+
+ /* ... times */
+ udf_timestamp_to_timespec(atime, &stat->st_atimespec);
+ udf_timestamp_to_timespec(mtime, &stat->st_mtimespec);
+ udf_timestamp_to_timespec(attrtime, &stat->st_ctimespec);
+#ifndef NO_STAT_BIRTHTIME
+ udf_timestamp_to_timespec(ctime, &stat->st_birthtimespec);
+#endif
+
+ /* ... sizes */
+ stat->st_size = inf_len;
+ stat->st_blksize = udf_node->udf_log_vol->lb_size;
+
+ /* special: updatables */
+ stat->st_nlink = link_cnt;
+ stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */
+ return;
+}
+
+
+static void udf_node_set_fileinfo(struct udf_node *udf_node, union dscrptr *dscrptr) {
+ struct stat *stat;
+ struct file_entry *file_entry;
+ struct extfile_entry *extfile_entry;
+ struct timestamp *atime, *mtime, *ctime, *attrtime;
+ uint64_t inf_len, unique_id;
+ uint32_t uid, gid, udf_perm;
+ uint16_t fe_tag, serial_num, link_cnt;
+ uint8_t filetype;
+
+ assert(udf_node);
+ assert(dscrptr);
+ stat = &udf_node->stat;
+
+ /* set (parts of) the stat structure */
+ /* XXX important info missing still like times etc. XXX */
+ uid = stat->st_uid;
+ gid = stat->st_gid;
+ inf_len = stat->st_size;
+ udf_perm = unix_mode_to_udf_perm(stat->st_mode); /* conversion to UDF perm. */
+
+ filetype = udf_node->udf_filetype;
+ unique_id = udf_node->unique_id;
+ serial_num = udf_node->serial_num;
+ link_cnt = udf_node->link_cnt;
+
+ /* check if its to be written in an normal file entry or a extended file entry ICB */
+ fe_tag = udf_rw16(dscrptr->tag.id);
+ if (fe_tag == TAGID_FENTRY) {
+ file_entry = &dscrptr->fe;
+#if 0
+ prev_direct_entries = udf_rw32(file_entry->icbtag.prev_num_dirs);
+ strat_param16 = udf_rw16(* (uint16_t *) (file_entry->icbtag.strat_param));
+ entries = udf_rw16(file_entry->icbtag.max_num_entries);
+ strategy = udf_rw16(file_entry->icbtag.strat_type);
+ data_length = udf_rw32(file_entry->l_ad);
+ addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea);
+#endif
+ file_entry->icbtag.file_type = filetype;
+ file_entry->inf_len = udf_rw64(inf_len);
+ file_entry->uid = udf_rw32(uid);
+ file_entry->gid = udf_rw32(gid);
+ file_entry->perm = udf_rw32(udf_perm);
+ file_entry->tag.serial_num = udf_rw16(serial_num);
+ file_entry->link_cnt = udf_rw16(link_cnt);
+ file_entry->unique_id = udf_rw64(unique_id);
+ atime = &file_entry->atime;
+ mtime = &file_entry->mtime;
+ ctime = mtime; /* XXX assumption */
+ attrtime = &file_entry->attrtime;
+ } else if (fe_tag == TAGID_EXTFENTRY) {
+ extfile_entry = &dscrptr->efe;
+#if 0
+ prev_direct_entries = udf_rw32(extfile_entry->icbtag.prev_num_dirs);
+ strat_param16 = udf_rw16(* (uint16_t *) (extfile_entry->icbtag.strat_param));
+ entries = udf_rw16(extfile_entry->icbtag.max_num_entries);
+ strategy = udf_rw16(extfile_entry->icbtag.strat_type);
+ data_length = udf_rw32(extfile_entry->l_ad);
+ addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea);
+#endif
+ extfile_entry->icbtag.file_type = filetype;
+ extfile_entry->inf_len = udf_rw64(inf_len);
+ extfile_entry->uid = udf_rw32(uid);
+ extfile_entry->gid = udf_rw32(gid);
+ extfile_entry->perm = udf_rw32(udf_perm);
+ extfile_entry->tag.serial_num = udf_rw16(serial_num);
+ extfile_entry->link_cnt = udf_rw16(link_cnt); /* how many FID's are linked to this (ext)fentry */
+ extfile_entry->unique_id = udf_rw64(unique_id); /* unique file ID */
+ atime = &extfile_entry->atime;
+ mtime = &extfile_entry->mtime;
+ ctime = &extfile_entry->ctime;
+ attrtime = &extfile_entry->attrtime;
+ } else {
+ printf("udf_node_set_file_info : help! i can't be here!!! i got a %d tag\n", fe_tag);
+ udf_dump_descriptor(dscrptr);
+ return;
+ }
+ /* FILL in {atime, mtime, attrtime} TIMES! */
+#ifndef NO_STAT_BIRTHTIME
+ udf_timespec_to_timestamp(&stat->st_birthtimespec, ctime);
+#endif
+ udf_timespec_to_timestamp(&stat->st_atimespec, atime);
+ udf_timespec_to_timestamp(&stat->st_mtimespec, mtime);
+ udf_timespec_to_timestamp(&stat->st_ctimespec, attrtime);
+
+ return;
+}
+
+
+/* with 32 bits ino_t, a maximum of about 8 TB discs are supported (1<<32) * 2KB*/
+ino_t udf_calc_hash(struct long_ad *icbptr) {
+ /* TODO unique file-id would be better */
+ return (ino_t) udf_rw32(icbptr->loc.lb_num);
+}
+
+
+void udf_insert_node_in_hash(struct udf_node *udf_node) {
+ struct udf_log_vol *log_vol;
+ uint32_t bucket;
+ ino_t hashkey;
+ struct long_ad icb;
+
+ icb.loc.lb_num = udf_rw32(TAILQ_FIRST(&udf_node->dscr_allocs)->lb_num);
+
+ log_vol = udf_node->udf_log_vol;
+ hashkey = udf_calc_hash(&icb);
+ udf_node->hashkey = hashkey;
+ bucket = hashkey & UDF_INODE_HASHMASK;
+ LIST_INSERT_HEAD(&log_vol->udf_nodes[bucket], udf_node, next_node);
+}
+
+
+/* dispose udf_node's administration */
+void udf_dispose_udf_node(struct udf_node *udf_node) {
+ struct udf_allocentry *alloc_entry;
+ struct udf_buf *buf_entry;
+ struct udf_node *lookup;
+ uint32_t bucket;
+ ino_t hashkey;
+
+ if (!udf_node) return;
+
+/* XXX locks? XXX */
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+
+ if (udf_node->dirty) {
+ DEBUG(printf("Warning: disposing dirty node\n"));
+ udf_node_unmark_dirty(udf_node);
+ }
+
+ /* free all associated buffers */
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ /* due to this `trick' we don't need to use a marker */
+ while ((buf_entry = TAILQ_FIRST(&udf_node->vn_bufs))) {
+ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */
+ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */
+ udf_detach_buf_from_node(udf_node, buf_entry);
+ udf_free_buf_entry(buf_entry);
+ }
+ /* free in-node filedata blob if present */
+ if (udf_node->intern_data)
+ free(udf_node->intern_data);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ /* free our extended attribute administration if present */
+ if (udf_node->extattrfile_icb)
+ free(udf_node->extattrfile_icb);
+ if (udf_node->streamdir_icb)
+ free(udf_node->streamdir_icb);
+ if (udf_node->extended_attr)
+ free(udf_node->extended_attr);
+
+ /* free dscr_allocs queue */
+ while ((alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs))) {
+ TAILQ_REMOVE(&udf_node->dscr_allocs, alloc_entry, next_alloc);
+ free(alloc_entry);
+ }
+
+ /* free allocation queue */
+ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) {
+ TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc);
+ free(alloc_entry);
+ }
+
+ /* if its part of a logical volume, delete it in its hash table */
+ if (udf_node->udf_log_vol) {
+ hashkey = udf_node->hashkey;
+ bucket = hashkey & UDF_INODE_HASHMASK;
+ LIST_FOREACH(lookup, &udf_node->udf_log_vol->udf_nodes[bucket], next_node) {
+ /* hashkey doesn't matter; just remove same udf_node pointer */
+ if (lookup == udf_node) {
+ assert(lookup->hashkey == hashkey);
+ DEBUG(printf("removal of udf_node from the hash table\n"));
+ LIST_REMOVE(lookup, next_node);
+ break;
+ }
+ }
+ }
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ /* free the node */
+ free(udf_node);
+}
+
+
+uint64_t udf_increment_unique_id(struct udf_log_vol *udf_log_vol) {
+ uint64_t unique_id, next_unique_id;
+
+ /* lock? */
+ unique_id = udf_log_vol->next_unique_id;
+
+ /* increment according to UDF 3/3.2.1.1 */
+ next_unique_id = unique_id + 1;
+ if (((next_unique_id << 32) >> 32) < 16) next_unique_id |= 16;
+
+ udf_log_vol->next_unique_id = next_unique_id;
+ DEBUG(printf("next unique_id <-- %"PRIu64"\n", udf_log_vol->next_unique_id));
+
+ return unique_id;
+}
+
+
+int udf_init_udf_node(struct udf_mountpoint *mountpoint, struct udf_log_vol *udf_log_vol, char *what, struct udf_node **udf_nodeptr) {
+ struct udf_node *udf_node;
+ uint32_t lb_size, data_space_avail;
+ int descr_ver;
+
+ what = what; /* not used yet */
+
+ assert(udf_log_vol);
+ lb_size = udf_log_vol->lb_size;
+
+ /* get ourselves some space */
+ udf_node = calloc(1, sizeof(struct udf_node));
+ if (!udf_node) return ENOMEM;
+
+ /* setup basic udf_node */
+ udf_node->addr_type = UDF_ICB_LONG_ALLOC;
+ udf_node->icb_len = sizeof(struct long_ad);
+
+ udf_node->serial_num = 1;
+ udf_node->udf_icbtag_flags = UDF_ICB_INTERN_ALLOC;
+ udf_node->link_cnt = 1; /* how many FID's are linked to this node */
+ udf_node->unique_id = 0; /* unique file ID unknown */
+
+ /* get internal space available for internal nodes */
+ /* TODO keep in mind the extended attributes! XXX */
+ descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver);
+ if (descr_ver == 2) {
+ /* TODO reserve some space for the icb creation time */
+ data_space_avail = udf_log_vol->lb_size - sizeof(struct file_entry) - 0; /* udf_rw32(file_entry->l_ea) */
+ } else {
+ data_space_avail = udf_log_vol->lb_size - sizeof(struct extfile_entry) - 0; /* udf_rw32(extfile_entry->l_ea) */
+ }
+ udf_node->intern_free = data_space_avail;
+ udf_node->intern_data = NULL;
+ udf_node->intern_len = 0;
+
+ /* finalise udf_node */
+ udf_node->mountpoint = mountpoint;
+ udf_node->udf_log_vol = udf_log_vol;
+ TAILQ_INIT(&udf_node->dscr_allocs);
+ TAILQ_INIT(&udf_node->alloc_entries);
+ TAILQ_INIT(&udf_node->vn_bufs);
+ UDF_MUTEX_INIT(&udf_node->alloc_mutex);
+ UDF_MUTEX_INIT(&udf_node->buf_mutex);
+
+ /* XXX udf_node_lock NOT USED XXX */
+ /* pthread_rwlock_init(&udf_node->udf_node_lock, NULL); */
+
+ *udf_nodeptr = udf_node;
+ return 0;
+}
+
+
+int udf_allocate_udf_node_on_disc(struct udf_node *udf_node) {
+ struct udf_allocentry *alloc_entry;
+ uint32_t lb_num, lb_size;
+ uint16_t vpart_num;
+ int error;
+
+ assert(udf_node);
+ assert(udf_node->udf_log_vol);
+ assert(udf_node->udf_log_vol->log_vol);
+
+ lb_size = udf_node->udf_log_vol->lb_size;
+ assert(lb_size);
+
+ /* pre-allocate node; its needed in directory linkage for now */
+ error = udf_allocate_lbs(udf_node->udf_log_vol, UDF_C_NODE, /*num lb */ 1, "New FID", &vpart_num, &lb_num, NULL);
+ if (error) return error;
+
+ alloc_entry = calloc(1, sizeof(struct udf_allocentry));
+ if (!alloc_entry) {
+ return ENOMEM;
+ }
+
+ alloc_entry->len = lb_size;
+ alloc_entry->vpart_num = vpart_num;
+ alloc_entry->lb_num = lb_num;
+ alloc_entry->flags = 0;
+ TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, alloc_entry, next_alloc);
+
+ assert(error == 0);
+ return error;
+}
+
+
+/* note: the udf_node (inode) is not stored in a hashtable! hash value is still invalid */
+/* TODO remember our extended attributes and remember our streamdir long_ad */
+int udf_readin_anon_udf_node(struct udf_log_vol *udf_log_vol, union dscrptr *given_dscrptr, struct long_ad *udf_icbptr, char *what, struct udf_node **udf_nodeptr) {
+ union dscrptr *dscrptr;
+ struct udf_node *udf_node;
+ struct udf_allocentry *alloc_entry;
+ struct udf_allocentry *cur_alloc, *next_alloc;
+ struct file_entry *file_entry;
+ struct extfile_entry *extfile_entry;
+ struct alloc_ext_entry *alloc_ext_entry;
+ struct long_ad *l_ad;
+ struct short_ad *s_ad;
+ uint64_t inf_len, calculated_len;
+ uint32_t lb_size, entries;
+ uint64_t data_length;
+ uint32_t data_space_avail;
+ uint32_t fe_tag;
+ uint64_t len;
+ uint32_t lb_num, vpart_num;
+ uint32_t icb_len;
+ int16_t addr_type;
+ uint8_t *file_data_pos;
+ uint8_t *pos;
+ uint8_t flags;
+ int error, advance_sector;
+
+ if ((udf_icbptr->loc.lb_num == 0) && (udf_icbptr->loc.part_num == 0) && (udf_icbptr->len == 0)) return ENOENT;
+
+ DEBUG(printf("udf_readin_anon_udf_node for %s\n", what));
+
+ error = udf_init_udf_node(/*mountpoint*/ NULL, udf_log_vol, what, &udf_node);
+ DEBUG(
+ if (error) printf("While reading in `anononymous' udf_node : got error %s\n", strerror(error));
+ );
+ if (error) return error;
+
+ *udf_nodeptr = udf_node;
+
+ assert(udf_log_vol);
+ lb_size = udf_log_vol->lb_size;
+
+ /* read in descriptor if not provided to us */
+ dscrptr = NULL;
+ len = lb_size; /* nodes are defined in lb_size only (?) */
+ lb_num = udf_rw32(udf_icbptr->loc.lb_num);
+ vpart_num = udf_rw16(udf_icbptr->loc.part_num);
+ if (!given_dscrptr) {
+ error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, lb_num, what, &dscrptr, NULL);
+ if (error) {
+ *udf_nodeptr = NULL;
+ return error;
+ }
+ } else {
+ dscrptr = given_dscrptr;
+ }
+
+ fe_tag = udf_rw16(dscrptr->tag.id);
+ if (fe_tag != TAGID_FENTRY && fe_tag != TAGID_EXTFENTRY) {
+ /* something wrong with this address; it doesn't start with a file entry */
+ printf("UDF: bad udf_node for %s; got a %d tag\n", what, fe_tag);
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ udf_dispose_udf_node(udf_node);
+ *udf_nodeptr = NULL;
+ return EFAULT;
+ }
+
+ /* get as much info as possible */
+ udf_node_get_fileinfo(udf_node, dscrptr);
+
+ /* reset pending write count */
+ udf_node->v_numoutput = 0;
+
+ /* initialise various variables for extracting allocation information */
+ inf_len = 0; data_length = 0; lb_num = 0; len = 0; vpart_num = 0; pos = NULL;
+ data_space_avail = 0;
+
+ next_alloc = calloc(1, sizeof(struct udf_allocentry));
+ if (!next_alloc) {
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ udf_dispose_udf_node(udf_node);
+ *udf_nodeptr = NULL;
+ return ENOMEM;
+ }
+ next_alloc->len = lb_size;
+ next_alloc->lb_num = udf_rw32(udf_icbptr->loc.lb_num);
+ next_alloc->vpart_num = udf_rw16(udf_icbptr->loc.part_num);
+
+ entries = 1;
+ file_data_pos = NULL;
+ calculated_len = 0;
+
+ error = 0;
+ addr_type = -1;
+ do {
+ cur_alloc = next_alloc;
+ next_alloc = calloc(1, sizeof(struct udf_allocentry));
+ if (!next_alloc) {
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ udf_dispose_udf_node(udf_node);
+ *udf_nodeptr = NULL;
+ return ENOMEM;
+ }
+ memcpy(next_alloc, cur_alloc, sizeof(struct udf_allocentry));
+ TAILQ_INSERT_TAIL(&udf_node->dscr_allocs, cur_alloc, next_alloc);
+
+ /* process this allocation descriptor */
+ /* note that we don't store the file descriptors -> XXX impl. use stuff gets lost here */
+ fe_tag = udf_rw16(dscrptr->tag.id);
+ switch (fe_tag) {
+ case TAGID_FENTRY :
+ /* allocation descriptors follow this tag */
+ file_entry = &dscrptr->fe;
+ entries = udf_rw16(file_entry->icbtag.max_num_entries);
+ data_length = udf_rw32(file_entry->l_ad);
+ pos = &file_entry->data[0] + udf_rw32(file_entry->l_ea);
+ inf_len = udf_rw64(file_entry->inf_len);
+ addr_type = udf_rw16(file_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ data_space_avail = lb_size - sizeof(struct file_entry) - udf_rw32(file_entry->l_ea);
+ /* process extended attributes */
+ /* keep a trace of the descriptors in this udf_node */
+ UDF_VERBOSE_MAX(udf_dump_file_entry(file_entry));
+ break;
+ case TAGID_EXTFENTRY :
+ /* allocation descriptors follow this tag */
+ extfile_entry = &dscrptr->efe;
+ entries = udf_rw16(extfile_entry->icbtag.max_num_entries);
+ data_length = udf_rw32(extfile_entry->l_ad);
+ pos = &extfile_entry->data[0] + udf_rw32(extfile_entry->l_ea);
+ inf_len = udf_rw64(extfile_entry->inf_len);
+ addr_type = udf_rw16(extfile_entry->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ data_space_avail = lb_size - sizeof(struct extfile_entry) - udf_rw32(extfile_entry->l_ea);
+ /* process extended attributes */
+ /* keep a trace of the descriptors in this udf_node */
+ UDF_VERBOSE_MAX(udf_dump_extfile_entry(extfile_entry));
+ break;
+ case TAGID_ALLOCEXTENT :
+ /* allocation descriptors follow this tag; treat as if continuation of a (ext)file entry*/
+ alloc_ext_entry = &dscrptr->aee;
+ data_length = udf_rw32(alloc_ext_entry->l_ad);
+ pos = &alloc_ext_entry->data[0];
+ assert(addr_type >= 0);
+
+ /* keep a trace of the descriptors in this udf_node */
+ UDF_VERBOSE_MAX(udf_dump_alloc_extent(alloc_ext_entry, addr_type));
+ break;
+ case TAGID_INDIRECT_ENTRY :
+ printf("create_anon_udf_node called with indirect entry; following chain\n");
+ l_ad = &dscrptr->inde.indirect_icb;
+ next_alloc->len = udf_rw32(l_ad->len);
+ next_alloc->lb_num = udf_rw32(l_ad->loc.lb_num);
+ next_alloc->vpart_num = udf_rw16(l_ad->loc.part_num);
+ entries = 1; /* at least one more entry */
+ advance_sector = 0; /* don't advance to next sector */
+
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ break;
+ default:
+ printf("read_file_part_extents: i can't be here! (tag = %d) in ICB hiargy of %s\n", fe_tag, what);
+ udf_dump_descriptor(dscrptr);
+ return EFAULT;
+ }
+ if (fe_tag != TAGID_INDIRECT_ENTRY) {
+ udf_node->addr_type = addr_type;
+ if (addr_type == UDF_ICB_INTERN_ALLOC) {
+ udf_node->intern_len = inf_len;
+ udf_node->intern_free = data_space_avail;
+ udf_node->intern_data = calloc(1, udf_node->intern_free);
+ if (udf_node->intern_data) {
+ memcpy(udf_node->intern_data, pos, inf_len);
+ } else {
+ error = ENOMEM;
+ }
+
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ return error;
+ }
+
+ icb_len = 0;
+ switch (udf_node->addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ icb_len = sizeof(struct short_ad);
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ icb_len = sizeof(struct long_ad);
+ break;
+ default :
+ printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type);
+ break;
+ }
+ udf_node->icb_len = icb_len;
+ advance_sector = 1;
+ while (icb_len && data_length) {
+ switch (udf_node->addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ s_ad = (struct short_ad *) pos;
+ len = udf_rw32(s_ad->len);
+ lb_num = udf_rw32(s_ad->lb_num);
+ vpart_num = cur_alloc->vpart_num;
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ l_ad = (struct long_ad *) pos;
+ len = udf_rw32(l_ad->len);
+ lb_num = udf_rw32(l_ad->loc.lb_num);
+ vpart_num = udf_rw16(l_ad->loc.part_num);
+ if (l_ad->impl.im_used.flags & UDF_ADIMP_FLAGS_EXTENT_ERASED) {
+ printf("UDF: got a `extent erased' flag in a file's long_ad; ignoring\n");
+ }
+ break;
+ default :
+ printf("UDF encountered an unknown allocation type %d\n", udf_node->addr_type);
+ break;
+ }
+ /* ecma-167 48.14.1.1 */
+ flags = (uint8_t) ((uint32_t) (len >> 30) & 3);
+ len = len & ((1<<30)-1);
+ if (flags == UDF_SPACE_REDIRECT) {
+ /* fill in next extent */
+ next_alloc->len = len;
+ next_alloc->lb_num = lb_num;
+ next_alloc->vpart_num = vpart_num;
+ advance_sector = 0; /* don't advance to next sector */
+ icb_len = data_length; /* must be last */
+ printf("Continuing extent flagged at vpart = %d, lb_num = %d, len = %d\n", (int) vpart_num, (int) lb_num, (int) len);
+ } else {
+ if (len) {
+ alloc_entry = calloc(1, sizeof(struct udf_allocentry));
+ if (!alloc_entry) {
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ return ENOMEM;
+ }
+ alloc_entry->len = len;
+ alloc_entry->lb_num = lb_num;
+ alloc_entry->vpart_num = vpart_num;
+ alloc_entry->flags = flags;
+ TAILQ_INSERT_TAIL(&udf_node->alloc_entries, alloc_entry, next_alloc);
+ }
+ calculated_len += len;
+ }
+ data_length -= icb_len;
+ pos += icb_len;
+ } /* while */
+ if (advance_sector) {
+ /* Note: UDF descriptor length is maximised to one sector */
+ next_alloc->lb_num++; /* advance one sector */
+ entries--;
+ }
+ } /* indirect ICB check */
+ if (entries) {
+ /* load in new dscrptr */
+ if (dscrptr != given_dscrptr) free(dscrptr);
+ error = udf_read_logvol_descriptor(udf_log_vol, next_alloc->vpart_num, next_alloc->lb_num, what, &dscrptr, NULL);
+ }
+ } while (entries && !error);
+
+ /* error from reading next sector in extent is not considered an error */
+ if (dscrptr != given_dscrptr && dscrptr) free(dscrptr);
+
+ if (calculated_len != (uint64_t) udf_node->stat.st_size) {
+ printf("UDF: create node length mismatch; stated as %g but calculated as %g bytes. fixing\n", (double) udf_node->stat.st_size, (double) calculated_len);
+ udf_node->stat.st_size = calculated_len;
+ }
+
+ return 0;
+}
+
+
+int udf_readin_udf_node(struct udf_node *dir_node, struct long_ad *udf_icbptr, struct fileid_desc *fid, struct udf_node **res_sub_node) {
+ struct udf_node *sub_node;
+ char *fid_name;
+ char entry_name[NAME_MAX];
+ uint32_t bucket;
+ ino_t hashkey;
+ int error;
+
+ assert(dir_node);
+ assert(udf_icbptr);
+ assert(fid);
+ assert(res_sub_node);
+
+ /* check if its allready in the logical volume's udf_node cache (inodes) */
+ hashkey = udf_calc_hash(udf_icbptr);
+ bucket = hashkey & UDF_INODE_HASHMASK;
+
+ LIST_FOREACH(sub_node, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) {
+ if (sub_node->hashkey == hashkey) {
+ *res_sub_node = sub_node;
+ DEBUG(printf("found node in hashlist\n"));
+
+ return 0;
+ }
+ }
+
+ /* dump the FID we are trying to read in */
+ UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) fid));
+
+ fid_name = (char *) fid->data + udf_rw16(fid->l_iu);
+ udf_to_unix_name(entry_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset);
+
+ /* build missing vnode */
+ error = udf_readin_anon_udf_node(dir_node->udf_log_vol, NULL, udf_icbptr, entry_name, &sub_node);
+ if (error)
+ return error;
+
+ if (!sub_node) {
+ printf("sub_node = NULL? and no error? \n");
+ }
+ assert(sub_node);
+
+ /* link this UDF node to the mountpoint and remember its hash-key */
+ sub_node->mountpoint = dir_node->mountpoint;
+ sub_node->hashkey = hashkey;
+
+ /* XXX use file version number and filechar from fid ? */
+ sub_node->file_version_num = udf_rw16(fid->file_version_num); /* user set */
+ sub_node->udf_filechar = fid->file_char;
+
+ /* insert/replace in mountpoint's udfnode hashtable with optional check for doubles */
+if (0) {
+ struct udf_node *lookup;
+ LIST_FOREACH(lookup, &dir_node->udf_log_vol->udf_nodes[bucket], next_node) {
+ if (lookup->hashkey == sub_node->hashkey) printf("DOUBLE hashnode?\n");
+ }
+}
+
+ LIST_INSERT_HEAD(&dir_node->udf_log_vol->udf_nodes[bucket], sub_node, next_node);
+ DEBUG(printf("inserting hash node for hash = %d\n", (int) hashkey));
+
+ *res_sub_node = sub_node;
+ return 0;
+}
+
+
+void udf_syncnode_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) {
+ /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */
+
+ wrcallback = wrcallback; /* unused for now */
+ sectordata = sectordata;
+
+ if (reason == UDF_WRCALLBACK_REASON_PENDING) {
+ /* what to do? */
+ return;
+ }
+ if (reason == UDF_WRCALLBACK_REASON_ANULATE) {
+ /* what to do? */
+ return;
+ }
+ assert(reason == UDF_WRCALLBACK_REASON_WRITTEN);
+ if (error) {
+ printf("UDF error: syncnode writing failed, not fixing yet!\n");
+ return;
+ }
+}
+
+
+/* XXX This mark node dirty code will simplify if we store nodes in buffers? XXX */
+void udf_node_mark_dirty(struct udf_node *udf_node) {
+ struct udf_allocentry *alloc_entry, *my_entry, *tail_entry;
+ struct udf_node *search_node, *tail_node;
+
+ if (udf_node->dirty) return;
+
+ my_entry = TAILQ_FIRST(&udf_node->dscr_allocs);
+ assert(my_entry);
+
+ /* dscr locks ? */
+ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex);
+if (1) {
+ /* insertion sort :-S */
+ tail_node = TAILQ_LAST(&udf_node->udf_log_vol->dirty_nodes, udf_node_list);
+ if (!tail_node) {
+ TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty);
+ } else {
+ tail_entry = TAILQ_FIRST(&tail_node->dscr_allocs);
+ if (tail_entry->lb_num < my_entry->lb_num) {
+ TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty);
+ } else {
+ /* find my place; could be done smarter */
+ TAILQ_FOREACH(search_node, &udf_node->udf_log_vol->dirty_nodes, next_dirty) {
+ alloc_entry = TAILQ_FIRST(&tail_node->dscr_allocs);
+ if (alloc_entry->lb_num > my_entry->lb_num) {
+ TAILQ_INSERT_BEFORE(search_node, udf_node, next_dirty);
+ break; /* foreach */
+ }
+ }
+ }
+ }
+} else {
+ /* dumb */
+ TAILQ_INSERT_TAIL(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty);
+}
+ UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex);
+ udf_node->dirty = 1;
+}
+
+
+static void udf_node_unmark_dirty(struct udf_node *udf_node) {
+ if (!udf_node->dirty) return;
+
+ /* remove me just in case; why were we called otherwise? */
+ UDF_MUTEX_LOCK(&udf_node->udf_log_vol->dirty_nodes_mutex);
+ TAILQ_REMOVE(&udf_node->udf_log_vol->dirty_nodes, udf_node, next_dirty);
+ UDF_MUTEX_UNLOCK(&udf_node->udf_log_vol->dirty_nodes_mutex);
+ udf_node->dirty = 0;
+}
+
+
+/* VOP_FSYNC : data FDATASYNC */
+int udf_sync_udf_node(struct udf_node *udf_node, char *why) {
+
+ DEBUG(printf("Syncing udf_node `%"PRIu64"` because of %s\n", udf_node->unique_id, why));
+ DEBUG(printf("sync udf node: dirty = %d, v_numoutput = %d\n", udf_node->dirty, udf_node->v_numoutput));
+ if (!udf_node->dirty) {
+ /* Not dirty??!!! */
+ udf_node_unmark_dirty(udf_node);
+ return 0;
+ }
+
+ if (!udf_node->udf_log_vol->writable) {
+ fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n");
+ exit(1);
+ }
+
+ /*
+ * We are really syncing disc but we are only continueing when the
+ * node itself is clean... not breaking the semantics.
+ */
+ /* XXX flushall flag magic XXX */
+ udf_bufcache->flushall = 1;
+ udf_purgethread_kick("Sync node");
+ fflush(stdout);
+
+ /* wait until all dirty bufs associated with this node are processed */
+ if (!udf_node->dirty) return 0;
+
+ if (udf_node->v_numoutput) {
+ usleep(100);
+ }
+ if (!udf_node->v_numoutput) return 0;
+
+ UDF_VERBOSE(printf("(wait on node)"));
+ while (udf_node->v_numoutput) {
+ usleep(100);
+ }
+
+ return 0;
+}
+
+
+extern void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size);
+
+/* VOP_FSYNC : metadata FFILESYNC */
+/* writeout the udf_node back to disc */
+/* TODO don't forget to writeout our extended attributes and write out the link to the associated streamdir as well */
+int udf_writeout_udf_node(struct udf_node *udf_node, char *why) {
+ struct udf_wrcallback wr_callback;
+ struct udf_allocentry *dscr_entry, *next_dscr_entry, *alloc_entry;
+ union dscrptr *dscrptr;
+ struct file_entry *fe;
+ struct extfile_entry *efe;
+ struct alloc_ext_entry *aee;
+ struct lb_addr parent_icb;
+ struct icb_tag *icbtag;
+ struct long_ad *l_ad;
+ struct short_ad *s_ad;
+ uint32_t *l_adptr; /* points to length of alloc. descr. */
+ uint8_t *pos;
+ char *what;
+ uint32_t alloc_entries;
+ uint64_t rest; /* in bytes */
+ uint64_t len;
+ uint64_t logblks_rec;
+ uint32_t lb_size, descr_ver;
+
+ if (!udf_node->udf_log_vol->writable) {
+ fprintf(stderr, "encountered a dirty node on a read-only filingsystem!\n");
+ exit(1);
+ }
+
+ lb_size = udf_node->udf_log_vol->lb_size;
+
+ /* assure all file data is written out! and clean up */
+ udf_sync_udf_node(udf_node, why);
+
+ /* XXX node lock? XXX */
+
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ /* clean up allocentry queue so we get a nice clean layout */
+ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ /* allocate for descriptor */
+ dscrptr = calloc(1, lb_size); /* not a bit biggish? */
+ if (!dscrptr) return ENOMEM;
+
+ /* calculate logical blocks recorded; zero for interns [UDF 2.3.6.5, ECMA 4/14.9.11, 4/14.6.8] */
+ logblks_rec = 0;
+ if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) {
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) {
+ if (alloc_entry->flags == UDF_SPACE_ALLOCATED) {
+ logblks_rec += (alloc_entry->len + lb_size-1) / lb_size;
+ }
+ }
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ }
+
+ /* fill in (ext)fentry descriptor */
+ /* copy descriptor version from the logvol's */
+
+ bzero(&parent_icb, sizeof(struct lb_addr));
+ descr_ver = udf_rw16(udf_node->udf_log_vol->log_vol->tag.descriptor_ver);
+ if (descr_ver == 3) {
+ efe = &dscrptr->efe;
+ efe->tag.id = udf_rw16(TAGID_EXTFENTRY);
+ efe->tag.descriptor_ver = udf_rw16(descr_ver);
+ efe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */
+ udf_set_imp_id(&efe->imp_id);
+ efe->logblks_rec = udf_rw64(logblks_rec);
+
+ /* set additional fileinfo (access, uid/gid) etc. */
+ udf_node_set_fileinfo(udf_node, dscrptr);
+ efe->obj_size = efe->inf_len; /* not true if there are streams [ECMA 4/48.17.11] */
+
+ icbtag = &efe->icbtag;
+ } else if (descr_ver == 2) {
+ fe = &dscrptr->fe;
+ fe->tag.id = udf_rw16(TAGID_FENTRY);
+ fe->tag.descriptor_ver = udf_rw16(descr_ver);
+ fe->ckpoint = udf_rw32(1); /* [ECMA 4/14.17.17] */
+ udf_set_imp_id(&fe->imp_id);
+ fe->logblks_rec = udf_rw64(logblks_rec);
+
+ /* set additional fileinfo (access, uid/gid) etc. */
+ udf_node_set_fileinfo(udf_node, dscrptr);
+ /* fe->obj_size doesn't exist */
+
+ icbtag = &fe->icbtag;
+ } else {
+ printf("UDF: i don't know this descriptor version %d\n", descr_ver);
+ return EBADF;
+ }
+
+ /* update derived info */
+ udf_node->udf_icbtag_flags = (udf_node->udf_icbtag_flags & ~UDF_ICB_TAG_FLAGS_ALLOC_MASK) | udf_node->addr_type;
+
+ /* strategy 4 has no parent nodes */
+ /* XXX NO extended attributes recorded YET (!!) XXX (like device nodes !!! ) */
+
+ dscr_entry = TAILQ_FIRST(&udf_node->dscr_allocs);
+
+ /* ensure allocation of (ext) file descriptor */
+ if (!dscr_entry) {
+ /* have to allocate one and add to queue ! */
+ printf("UDF: XXX no allocation of file descriptor entry yet in sync_udf_node\n");
+ return ENOENT;
+ }
+
+ /* XXX implement alloc entry walker? XXX */
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ what = "UDF sync: File descriptor";
+ alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries);
+ do {
+ assert(dscr_entry->len <= lb_size);
+ if (icbtag) {
+ /* fill in ICB fields */ /* XXX we're only writing out strategy type 4 !!! XXX */
+ icbtag->prev_num_dirs = udf_rw32(0); /* prev_alloc_entries = 0 due to strategy type 4 XXX */
+ icbtag->strat_type = udf_rw16(4); /* default UDF strategy type 4 XXX what about 4096? */
+ icbtag->parent_icb = parent_icb;
+ icbtag->file_type = udf_node->udf_filetype;
+ icbtag->flags = udf_rw16(udf_node->udf_icbtag_flags);
+ icbtag->max_num_entries = udf_rw16(1); /* due to strategy type 4 ! XXX */
+ }
+
+ pos = 0;
+ rest = 0;
+ l_adptr = NULL; /* invalid */
+ switch (udf_rw16(dscrptr->tag.id)) {
+ case TAGID_FENTRY :
+ /* not used now */
+ fe = &dscrptr->fe;
+ pos = &fe->data[0] + udf_rw32(fe->l_ea);
+ rest = dscr_entry->len - sizeof(struct file_entry) - udf_rw32(fe->l_ea);
+ l_adptr = &fe->l_ad;
+ break;
+ case TAGID_EXTFENTRY :
+ efe = &dscrptr->efe;
+ pos = &efe->data[0] + udf_rw32(efe->l_ea);
+ rest = dscr_entry->len - sizeof(struct extfile_entry) - udf_rw32(efe->l_ea);
+ l_adptr = &efe->l_ad;
+ break;
+ case TAGID_ALLOCEXTENT :
+ aee = &dscrptr->aee;
+ pos = &aee->data[0];
+ rest = dscr_entry->len - sizeof(struct alloc_ext_entry);
+ l_adptr = &aee->l_ad;
+ break;
+ case TAGID_INDIRECT_ENTRY :
+ /* do we even do these in strat 4 ? */
+ printf("UDF: sanity check; request for writeout of indirect entry\n");
+ free(dscrptr);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ return ENOENT; /* panic really */
+ }
+ assert(l_adptr);
+
+ /* fill in remaining allocation entries */
+ /* remember to keep one SPARE for extending */
+
+ alloc_entries = 0;
+ DEBUG(printf("Allocated at "));
+
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ /* internal allocation -> copy in data */
+ assert(udf_node->intern_len <= rest);
+
+ bzero(pos, rest);
+ memcpy(pos, udf_node->intern_data, udf_node->intern_len);
+
+ /* make sure the length for allocation entries is the same as the data it contains (4/8.8.2, 4/14.6.8) */
+ *l_adptr = udf_rw32(udf_node->intern_len);
+
+ /* alloc_entry is zero, so it'll fall trough */
+ pos += udf_node->intern_len; /* advance pos for length calculation */
+ alloc_entry = NULL; /* not applicable */
+ }
+
+ while ((rest > 2*udf_node->icb_len) && alloc_entry) {
+ assert(udf_node->icb_len);
+ DEBUG(printf("[%p, lb_num = %d, len = %d] ", alloc_entry, (uint32_t) alloc_entry->lb_num, (uint32_t) alloc_entry->len));
+
+ /* XXX assumption: UDF_SPACE_* is equal to UDF flags XXX */
+ len = alloc_entry->len | (((uint32_t) alloc_entry->flags) << 30);
+ switch (udf_node->addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ s_ad = (struct short_ad *) pos;
+ s_ad->len = udf_rw32(len);
+ s_ad->lb_num = udf_rw32(alloc_entry->lb_num);
+ assert(alloc_entry->vpart_num == 0);
+ if (alloc_entry->len == 0) s_ad->lb_num = udf_rw32(0);
+ if (alloc_entry->flags == UDF_SPACE_FREE) s_ad->lb_num = udf_rw32(0);
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ l_ad = (struct long_ad *) pos;
+ l_ad->len = udf_rw32(len);
+ l_ad->loc.lb_num = udf_rw32(alloc_entry->lb_num);
+ l_ad->loc.part_num = udf_rw16(alloc_entry->vpart_num);
+ l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id);
+ if (alloc_entry->len == 0) l_ad->loc.lb_num = udf_rw32(0);
+ if (alloc_entry->flags == UDF_SPACE_FREE) l_ad->loc.lb_num = udf_rw32(0);
+ break;
+ }
+ alloc_entries++;
+ *l_adptr = udf_rw32(alloc_entries * udf_node->icb_len);
+ alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc);
+ pos += udf_node->icb_len;
+ rest -= udf_node->icb_len;
+ }
+ DEBUG(printf("\nend alloc\n\n"));
+
+ next_dscr_entry = TAILQ_NEXT(dscr_entry, next_alloc);
+ if (alloc_entry) {
+ /* overflow */
+
+ /* ensure allocation of (ext) file descriptor */
+ if (!next_dscr_entry) {
+ /* have to allocate one and add to queue ! */
+ printf("UDF: XXX no allocation of allocation extent descriptor yet in sync_udf_node\n");
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ return ENOENT;
+ }
+ /* flag next extent being specified */
+ len = next_dscr_entry->len | ((uint32_t) UDF_SPACE_REDIRECT << 30);
+ switch (udf_node->addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ s_ad = (struct short_ad *) pos;
+ s_ad->len = udf_rw32(len);
+ s_ad->lb_num = udf_rw32(next_dscr_entry->lb_num);
+ assert(next_dscr_entry->vpart_num == 0);
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ l_ad = (struct long_ad *) pos;
+ l_ad->len = udf_rw32(len);
+ l_ad->loc.lb_num = udf_rw32(next_dscr_entry->lb_num);
+ l_ad->loc.part_num = udf_rw16(next_dscr_entry->vpart_num);
+ l_ad->impl.im_used.unique_id = udf_rw64(udf_node->unique_id);
+ break;
+ }
+ alloc_entries++;
+ pos += udf_node->icb_len;
+ rest -= udf_node->icb_len;
+ }
+ /* manage lengths */
+ dscrptr->tag.desc_crc_len = udf_rw16((pos - (uint8_t *) dscrptr) - UDF_DESC_TAG_LENGTH);
+
+ /* writeout */
+ wr_callback.function = udf_syncnode_callback;
+#if 0
+ wr_callback.structure = (void *) udf_node;
+ wr_callback.vpart_num = dscr_entry->vpart_num;
+ wr_callback.lb_num = dscr_entry->lb_num;
+ wr_callback.offset = 0; /* ? */
+ wr_callback.length = dscr_entry->len;
+#endif
+
+ UDF_VERBOSE_MAX(
+ udf_validate_tag_and_crc_sums(dscrptr); /* for dumping */
+ udf_dump_descriptor(dscrptr);
+ );
+ errno = udf_write_logvol_descriptor(udf_node->udf_log_vol, dscr_entry->vpart_num, dscr_entry->lb_num, what, dscrptr, &wr_callback);
+
+ /* advance */
+ if (alloc_entry) {
+ dscr_entry = next_dscr_entry;
+ assert(dscr_entry);
+
+ /* allocate and initialise new allocation extent descriptor */
+ /* also flag no icbtag follows */
+ bzero(dscrptr, lb_size);
+ aee = &dscrptr->aee;
+ aee->tag.id = udf_rw16(TAGID_ALLOCEXTENT);
+ aee->tag.descriptor_ver = udf_node->udf_log_vol->log_vol->tag.descriptor_ver;
+ icbtag = NULL;
+
+ what = "UDF sync: allocation extent";
+ }
+
+ } while (alloc_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ /* XXX Free extra non used allocation extent descriptors XXX */
+
+ /* Mark me clean */
+ if (udf_node->mountpoint) {
+ /* remove me just in case; why were we called otherwise? */
+ udf_node_unmark_dirty(udf_node);
+ }
+
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * Space bitmap reader and writer
+ *
+ ******************************************************************************************/
+
+/* tested OK */
+int udf_read_in_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size, uint64_t *freespace) {
+ struct udf_allocentry *alloc_entry;
+ uint64_t bits, from, now, start, end;
+ uint8_t byte, bit, bitpos, state, *pos;
+ int cnt;
+
+ assert(udf_rw16(sbd->tag.id) == TAGID_SPACE_BITMAP);
+
+ DEBUG(printf("processing space bitmap : \n"););
+ bits = udf_rw32(sbd->num_bits);
+
+ /*
+ * Mark the disc as completely full;
+ * Bugallert: use `udf_mark_allocentry_queue()' for the extent might
+ * not fit in just one alloc entry for bigger discs
+ */
+ assert(TAILQ_EMPTY(queue));
+ udf_mark_allocentry_queue(queue, lb_size, 0, bits * lb_size, UDF_SPACE_ALLOCATED, NULL, NULL);
+
+ pos = sbd->data;
+ from = 0; now = 0; bitpos = 0; byte = *pos; state = byte & 1;
+ *freespace = 0;
+ while (now < bits) {
+ if (bitpos == 0) {
+ byte = *pos++;
+ }
+ bit = byte & 1;
+ if (bit != state) {
+ if (state) {
+ start = from;
+ end = now-1;
+ /* printf("[%08d - %08d]", start, end); */
+ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start+1)*lb_size, UDF_SPACE_FREE, NULL, NULL);
+ *freespace += (end-start+1)*lb_size;
+ }
+ from = now;
+ state = bit;
+ }
+ byte >>= 1;
+ bitpos = (bitpos+1) & 7;
+ now++;
+ }
+ if (state) {
+ start = from;
+ end = now;
+ /* printf("[%08d - %08d]", start, end); */
+ udf_mark_allocentry_queue(queue, lb_size, start*lb_size, (end-start)*lb_size, UDF_SPACE_FREE, NULL, NULL);
+ *freespace += (end-start)*lb_size;
+ }
+
+ UDF_VERBOSE_TABLES(
+ printf("\t\tFree space found on this partition");
+ cnt = 0;
+ start = 0;
+ TAILQ_FOREACH(alloc_entry, queue, next_alloc) {
+ if (alloc_entry->flags == UDF_SPACE_ALLOCATED) {
+ /* printf("... "); */
+ } else {
+ if (cnt == 0) printf("\n\t\t\t");
+ printf("[%08"PRIu64" - %08"PRIu64"] ", start / lb_size, ((start + alloc_entry->len) / lb_size)-1);
+ cnt++; if (cnt > 4) cnt = 0;
+ }
+ start += alloc_entry->len;
+ }
+ printf("\n");
+ );
+
+ /* merge is not nessisary */
+ return 0;
+}
+
+
+/* inverse of readin space bitmap; it synchronises the bitmap with the queue */
+/* tested OK */
+int udf_sync_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size) {
+ struct udf_allocentry *alloc_entry;
+ uint32_t start, bits, total_bits;
+ uint32_t cnt, byte;
+ uint8_t bit, bitmask, setting;
+ uint8_t *pos;
+
+ /* merge it just in case */
+ udf_merge_allocentry_queue(queue, lb_size);
+
+ total_bits = udf_rw32(sbd->num_bits);
+ DEBUG(printf("SYNC SPACE BITMAP DEBUG: total bits = %d\n", total_bits));
+
+ alloc_entry = TAILQ_FIRST(queue);
+ start = alloc_entry->lb_num;
+ assert(start == 0);
+
+ TAILQ_FOREACH(alloc_entry, queue, next_alloc) {
+ DEBUG(printf(" [%d : %d + %d]", alloc_entry->flags, alloc_entry->lb_num, alloc_entry->len));
+ bits = alloc_entry->len / lb_size;
+ assert(bits*lb_size == alloc_entry->len);
+
+ byte = start / 8;
+ bit = start - byte*8;
+
+ pos = sbd->data + byte;
+
+ if (byte*8 + bit + bits > total_bits) { /* XXX > or >= ? */
+ /* this should NEVER happen */
+ printf("UDF: not enough space writing back space bitmap! HELP!\n");
+ return EBADF;
+ }
+
+ cnt = 0;
+ setting = (alloc_entry->flags != UDF_SPACE_FREE) ? 0 : 255;
+ while (cnt < bits) {
+ bitmask = (1 << bit);
+
+ /* simple sanity check */
+ if (byte*8 + bit >= total_bits) {
+ printf("IEEEE!!!! too big; %d instead of %d\n", (byte*8 + bit), total_bits);
+ }
+ *pos = (*pos & (~bitmask)) | (setting ? bitmask: 0);
+
+ cnt++;
+ bit++;
+ if (bit == 8) {
+ /* byte transition */
+ byte++; bit = 0;
+ pos++;
+#if 0
+ /* speedup by doing bytes at a time */
+ while (bits-cnt > 8) {
+ *pos = setting;
+ cnt += 8;
+ pos++; byte++;
+ }
+#endif
+ }
+ }
+ start += bits;
+ }
+ DEBUG(printf("\n\n"));
+ return 0;
+}
+
+
+
+/******************************************************************************************
+ *
+ * Filepart readers and writers
+ *
+ ******************************************************************************************/
+
+/* part of VOP_STRATEGY */
+/* internal function; reads in a new buffer */
+/* !!! bufcache lock ought to be held on entry !!! */
+int udf_readin_file_buffer(struct udf_node *udf_node, char *what, uint32_t sector, int cache_flags, struct udf_buf **buf_entry_p) {
+ struct udf_allocentry *alloc_entry;
+ struct udf_buf *buf_entry;
+ uint64_t cur_offset;
+ uint64_t overlap_length, overlap_sectors, transfer_length;
+ uint32_t lb_size;
+ uint32_t len, lb_num, vpart_num;
+ int32_t error;
+ uint8_t flags;
+
+ assert(udf_node);
+ assert(buf_entry_p);
+ assert(udf_bufcache->bufcache_lock.locked);
+
+ error = udf_get_buf_entry(udf_node, buf_entry_p);
+ if (error) return error;
+
+ buf_entry = *buf_entry_p;
+ lb_size = udf_node->udf_log_vol->lb_size;
+
+ /* internal node? */
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ buf_entry->b_lblk = 0;
+ buf_entry->b_flags = 0; /* not dirty, not needing alloc */
+ buf_entry->b_bcount = udf_node->intern_len;
+ buf_entry->b_resid = lb_size - udf_node->intern_len;
+
+ memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len);
+
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_attach_buf_to_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ return error;
+ }
+
+ /* `normal' node */
+
+ /* find sector in the allocation space */
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ cur_offset = 0;
+ error = EIO; /* until proven readable */
+ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) {
+ len = alloc_entry->len;
+ lb_num = alloc_entry->lb_num;
+ vpart_num = alloc_entry->vpart_num;
+ flags = alloc_entry->flags;
+
+ /* check overlap with this alloc entry */
+ if (cur_offset + len > sector * lb_size) {
+ /* found an extent that fits */
+ assert(((sector * lb_size - cur_offset) % lb_size) == 0); /* ought to be on sector boundary */
+ lb_num += sector - (cur_offset / lb_size);
+
+ overlap_length = cur_offset + len - sector * lb_size;
+ overlap_sectors = (overlap_length + lb_size -1) / lb_size;
+ transfer_length = MIN(lb_size, overlap_length); /* max one logical sector */
+
+ /* fill in new buf info */
+ buf_entry->b_lblk = sector;
+ buf_entry->b_bcount = transfer_length;
+ buf_entry->b_resid = lb_size - transfer_length;
+
+ /* sector transfered; mark valid */
+ buf_entry->b_flags = 0; /* not dirty and valid */
+
+ switch (flags) {
+ case UDF_SPACE_ALLOCATED :
+ /* on disc or in the write queue/cache to be written out; read one block at a time */
+ error = udf_read_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, what, buf_entry->b_data, overlap_sectors, cache_flags);
+ break;
+ case UDF_SPACE_FREE :
+ /* fall trough */
+ case UDF_SPACE_ALLOCATED_BUT_NOT_USED :
+ error = 0;
+ bzero(buf_entry->b_data, lb_size);
+ break;
+ default :
+ fprintf(stderr, "Got an redirect flag, can't happen\n");
+ break;
+ }
+
+ if (error) {
+ fprintf(stderr, "\tgot error from read_logvol_sector : %s\n", strerror(error));
+ break; /* FOREACH */
+ }
+
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_attach_buf_to_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ return 0;
+
+ } /* looking for overlap */
+
+ cur_offset += len;
+ } /* FOREACH */
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ *buf_entry_p = NULL;
+
+ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */
+ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */
+ udf_free_buf_entry(buf_entry);
+ return error;
+}
+
+
+/* internal function; shedule writing out of a buffer */
+/* part of VOP_STRATEGY */
+int udf_writeout_file_buffer(struct udf_node *udf_node, char *what, int cache_flags, struct udf_buf *buf_entry) {
+ struct udf_allocentry *from_alloc, *to_alloc, *alloc_entry;
+ uint32_t lb_num, lb_size, lblk;
+ uint16_t vpart_num;
+ int error;
+
+ what = what; /* not used now */
+
+ if (!udf_node->udf_log_vol->writable) {
+ /* ieek */
+ fprintf(stderr, "write request from non writable file buffer?\n");
+ }
+
+ /* first get logical block offset and block size */
+ lblk = buf_entry->b_lblk;
+ lb_size = udf_node->udf_log_vol->lb_size;
+
+ error = 0;
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ /* maybe a bit too strict locking; code is not winning the beauty contest */
+
+ /* check if we can convert/save disc space */
+ if (udf_node->stat.st_size <= udf_node->intern_free) {
+ /* convert to internal alloc with backup storage (writing there) */
+ if (udf_node->addr_type != UDF_ICB_INTERN_ALLOC) {
+ DEBUG(printf("CONVERTING to intern alloc\n"));
+ /* first free all previously allocated sectors (if any) */
+ error = udf_node_release_extent(udf_node, 0, udf_node->stat.st_size);
+ }
+ assert(!error);
+ if (!udf_node->intern_data) {
+ udf_node->intern_data = calloc(1, udf_node->intern_free);
+ }
+ if (udf_node->intern_data) {
+ assert(buf_entry->b_bcount <= udf_node->intern_free);
+
+ memcpy(udf_node->intern_data, buf_entry->b_data, buf_entry->b_bcount);
+ udf_node->intern_len = buf_entry->b_bcount;
+ udf_node->addr_type = UDF_ICB_INTERN_ALLOC;
+
+ /* we don't write here: mark buffer clean */
+ udf_mark_buf_clean(udf_node, buf_entry);
+ udf_mark_buf_allocated(udf_node, buf_entry); /* signal its allocated */
+ buf_entry->b_flags &= ~(B_ERROR);
+
+ /* check if all buffers are gone now and mark node for writeout to indicate state change */
+ assert(udf_node->v_numoutput == 0);
+ udf_node_mark_dirty(udf_node);
+
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ return 0;
+ }
+ /* fall trough ... for some reason it isn't converted */
+ } else {
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ DEBUG(printf("CONVERTING to normal alloc\n"));
+ /* won't fit anymore, have to turn it into a `normal' alloced */
+ udf_node->intern_len = 0;
+ if (udf_node->intern_data)
+ free(udf_node->intern_data);
+ udf_node->intern_data = NULL;
+ udf_node->icb_len = sizeof(struct long_ad);
+ udf_node->addr_type = UDF_ICB_LONG_ALLOC;
+
+ udf_node_mark_dirty(udf_node);
+
+ /* mark needalloc to make sure it gets an address */
+ udf_mark_buf_needing_allocate(udf_node, buf_entry); /* signal it needs allocation */
+ }
+ }
+
+ /* merge queue first to get better performance */
+ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size);
+
+ /* get extent that it represents */
+ udf_mark_allocentry_queue(&udf_node->alloc_entries, lb_size, (uint64_t) lblk*lb_size, buf_entry->b_bcount, UDF_SPACE_ALLOCATED, &from_alloc, &to_alloc);
+ alloc_entry = from_alloc;
+ if (buf_entry->b_flags & B_NEEDALLOC) {
+ /* need allocation */
+ error = udf_node_allocate_lbs(udf_node, /* num lb */ 1, &vpart_num, &lb_num, NULL);
+ assert(!error);
+ udf_mark_buf_allocated(udf_node, buf_entry);
+
+ alloc_entry->lb_num = lb_num;
+ alloc_entry->vpart_num = vpart_num;
+ }
+ assert(TAILQ_NEXT(alloc_entry, next_alloc) == to_alloc || (alloc_entry == to_alloc));
+
+ lb_num = alloc_entry->lb_num;
+ vpart_num = alloc_entry->vpart_num;
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ /* printf("writing out filebuffer vpart %d, lb_num %d for %s\n", vpart_num, lb_num, what); */
+ error = udf_write_logvol_sector(udf_node->udf_log_vol, vpart_num, lb_num, "File contents", buf_entry->b_data, cache_flags, NULL);
+
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ if (error) {
+ printf("YIKES error during writing of logvol_sector\n");
+ udf_mark_buf_needing_allocate(udf_node, buf_entry);
+ buf_entry->b_flags |= B_ERROR;
+ /* buf_entry->b_errno = error; */
+ } else {
+ udf_mark_buf_clean(udf_node, buf_entry);
+ buf_entry->b_flags &= ~(B_ERROR);
+ }
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ return error;
+}
+
+
+/* VOP_READ */
+int udf_read_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) {
+ struct udf_buf *buf_entry;
+ off_t offset;
+ uint64_t sector, lb_size, data_length;
+ uint8_t *base;
+ int error, short_buf;
+
+ if (!udf_node) return EINVAL;
+
+ /* NOTE: we are NOT marking the node dirty only by reading it */
+ udf_set_timespec_now(&udf_node->stat.st_atimespec);
+
+ if (udf_node->stat.st_size == 0) {
+ if (data_uio->uio_resid) return EIO; /* reading past end by default */
+ return 0;
+ }
+
+ lb_size = udf_node->udf_log_vol->lb_size;
+ error = 0;
+ while (data_uio->uio_resid) {
+ error = 0;
+ short_buf = 0;
+ sector = data_uio->uio_offset / lb_size;
+
+ /* lookup sector in buffer set */
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ udf_lookup_node_buf(udf_node, sector, &buf_entry);
+
+ if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) {
+ /* `page in' sector from file */
+ error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry);
+ }
+
+ if (!error && buf_entry) {
+ offset = data_uio->uio_offset - (off_t) sector*lb_size;
+ base = buf_entry->b_data;
+ if (offset >= 0) {
+ data_length = buf_entry->b_bcount - offset;
+ data_length = MIN(data_length, data_uio->uio_resid);
+ uiomove(base + offset, data_length, data_uio);
+ }
+ short_buf = (buf_entry->b_bcount < lb_size); /* short buf -> none will follow */
+ }
+ assert(!error || (error && !buf_entry));
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ if (error) break; /* while */
+
+ if (data_uio->uio_resid == 0) return 0; /* finished? */
+ if (short_buf) break; /* while */
+ } /* while */
+
+ if (data_uio->uio_resid) {
+ printf("UDF: WARNING file is truncated; missing %d bytes while reading %s\n",
+ (int) data_uio->uio_resid, what);
+ return EIO;
+ }
+
+ return error;
+}
+
+
+/* XXX meaning is to be changed; kept for reference still XXX */
+void udf_filepart_write_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) {
+ /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */
+
+ wrcallback = wrcallback; /* unused for now */
+ sectordata = sectordata;
+
+ if (reason == UDF_WRCALLBACK_REASON_PENDING) {
+ /* what to do? */
+ return;
+ }
+ if (reason == UDF_WRCALLBACK_REASON_ANULATE) {
+ /* what to do? */
+ return;
+ }
+ assert(reason == UDF_WRCALLBACK_REASON_WRITTEN);
+ if (error) {
+ printf("UDF error: file part write failed, not fixing yet!\n");
+ return;
+ }
+}
+
+
+/* VOP_TRUNCATE */
+/* doesn't lock udf_node */
+int udf_truncate_node(struct udf_node *udf_node, uint64_t length /* ,ioflags */) {
+ struct udf_log_vol *udf_log_vol;
+ struct udf_allocentry *alloc_entry, *cut_point;
+ struct udf_buf *buf_entry, *marker;
+ uint32_t lb_size;
+ uint64_t cur_extent, block_extent, new_extent, too_much;
+ uint32_t last_sector;
+ int32_t error;
+
+ if (!udf_node) return EINVAL;
+
+ if (udf_open_logvol(udf_node->udf_log_vol))
+ return EROFS;
+
+ udf_log_vol = udf_node->udf_log_vol;
+ lb_size = udf_log_vol->lb_size;
+
+ /* we might change this node AND access times ... */
+ if (!udf_node->dirty) {
+ /* mark node as dirty */
+ udf_node_mark_dirty(udf_node);
+ }
+
+/* XXX lock node !!! XXX */
+
+ /* merge known allocation entry queue */
+ new_extent = length;
+ block_extent = (uint64_t) lb_size * ((new_extent + lb_size-1) / lb_size); /* inclusive */
+ too_much = block_extent - new_extent;
+ assert(block_extent >= new_extent);
+
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size);
+
+ /* extend the file when nessisary */
+ if (new_extent > (uint64_t) udf_node->stat.st_size) {
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ /* XXX to seperate function XXX */
+ /* grow intern node by converting it to a normal buffer first */
+ /* First lookup if the node already had a buffer associated */
+ udf_lookup_node_buf(udf_node, 0, &buf_entry);
+ if (!buf_entry) {
+ error = udf_get_buf_entry(udf_node, &buf_entry);
+ if (error) {
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ return error;
+ }
+ buf_entry->b_lblk = 0;
+ buf_entry->b_flags = 0;
+
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_attach_buf_to_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ } else {
+ DEBUG(printf("found buffer associated with intern node!\n"));
+ }
+
+ buf_entry->b_bcount = MIN(lb_size, new_extent);
+ buf_entry->b_resid = lb_size - buf_entry->b_bcount;
+
+ memcpy(buf_entry->b_data, udf_node->intern_data, udf_node->intern_len);
+ memset(buf_entry->b_data + udf_node->intern_len, 0, lb_size - udf_node->intern_len);
+
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_mark_buf_dirty(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+
+ udf_node->intern_len = 0;
+ if (udf_node->intern_data)
+ free(udf_node->intern_data);
+ udf_node->intern_data = NULL;
+ udf_node->icb_len = sizeof(struct long_ad);
+ udf_node->addr_type = UDF_ICB_LONG_ALLOC;
+ }
+ udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent);
+ if (new_extent < block_extent) {
+ alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries);
+ assert(alloc_entry->len > too_much);
+
+ alloc_entry->len -= too_much;
+ }
+ /* XXX Andrey suggested buffer extension? XXX */
+ udf_node->stat.st_size = new_extent;
+ }
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ /* `free' the extent when shrinking the extent */
+ if (new_extent < (uint64_t) udf_node->stat.st_size) {
+ DEBUG(printf("TRIMMING file %"PRIu64" from %d to %d\n", udf_node->unique_id, (int32_t) udf_node->stat.st_size, (uint32_t) length));
+
+ /* free file buffers that are not needed anymore */
+ /* XXX NetBSD kernel : uvm_vnp_setsize() XXX */
+ marker = calloc(1, sizeof(struct udf_buf));
+ if (!marker) return ENOMEM;
+
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ /* use marker as we are going to delete items in the list we are traversing */
+ last_sector = new_extent / lb_size;
+ TAILQ_INSERT_HEAD(&udf_node->vn_bufs, marker, b_vnbufs);
+ while ((buf_entry = TAILQ_NEXT(marker, b_vnbufs))) {
+ /* advance marker */
+ TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs);
+ TAILQ_INSERT_AFTER(&udf_node->vn_bufs, buf_entry, marker, b_vnbufs);
+
+ /* process buf_entry */
+ if (buf_entry->b_lblk > last_sector) {
+ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */
+ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */
+ udf_detach_buf_from_node(udf_node, buf_entry);
+ udf_free_buf_entry(buf_entry);
+ }
+ /* trim last buffer entry */
+ /* XXX NetBSD kernel : uvm_vnp_zerorange(), vtruncbuf() XXX */
+ if (buf_entry->b_lblk == last_sector) {
+ buf_entry->b_bcount = udf_node->stat.st_size % lb_size;
+ buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount;
+ /* still data to record? */
+ if (buf_entry->b_bcount == 0) {
+ /* marker not an issue here */
+ udf_mark_buf_clean(udf_node, buf_entry); /* its destroyed so not dirty */
+ udf_mark_buf_allocated(udf_node, buf_entry); /* i.e. taken care of */
+ udf_detach_buf_from_node(udf_node, buf_entry);
+ udf_free_buf_entry(buf_entry);
+ }
+ }
+ }
+ TAILQ_REMOVE(&udf_node->vn_bufs, marker, b_vnbufs);
+ free(marker);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ /* shrink intern node */
+ too_much = udf_node->stat.st_size - new_extent;
+ udf_node->intern_len -= too_much;
+ udf_node->intern_free += too_much;
+ memset(udf_node->intern_data + new_extent, 0, too_much);
+ } else {
+ /* shrink alloc entries _after_ deleting bufs to make sure we are not crossed */
+ error = udf_node_release_extent(udf_node, block_extent, udf_node->stat.st_size);
+
+ /* after `cleanup', trim now unused entries */
+ udf_merge_allocentry_queue(&udf_node->alloc_entries, lb_size);
+
+ if (new_extent == 0) {
+ /* remove all; most common case too */
+ while ((alloc_entry = TAILQ_FIRST(&udf_node->alloc_entries))) {
+ TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc);
+ free(alloc_entry);
+ }
+ cur_extent = 0;
+ } else {
+ /* move code to udf_allocentries.c ? */
+ udf_cut_allocentry_queue(&udf_node->alloc_entries, lb_size, block_extent);
+
+ /* find cut-point */
+ cur_extent = 0;
+ TAILQ_FOREACH(alloc_entry, &udf_node->alloc_entries, next_alloc) {
+ cur_extent += alloc_entry->len;
+ if (cur_extent == block_extent) break;
+ }
+ cut_point = alloc_entry; /* all after this point need to be deleted */
+
+ assert(cut_point);
+ while ((alloc_entry = TAILQ_NEXT(cut_point, next_alloc))) {
+ TAILQ_REMOVE(&udf_node->alloc_entries, alloc_entry, next_alloc);
+ free(alloc_entry);
+ }
+
+ /* clip last entry to the correct size */
+ if (new_extent < block_extent) {
+ assert(too_much == block_extent - new_extent);
+
+ alloc_entry = TAILQ_LAST(&udf_node->alloc_entries, udf_alloc_entries);
+ assert(alloc_entry->len > too_much);
+
+ alloc_entry->len -= too_much;
+ cur_extent -= too_much;
+ }
+ }
+ assert(cur_extent == new_extent);
+ }
+ udf_node->stat.st_size = new_extent;
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+ }
+
+ return 0;
+}
+
+
+/* VOP_WRITE */
+int udf_write_file_part_uio(struct udf_node *udf_node, char *what, int content, struct uio *data_uio) {
+ struct udf_buf *buf_entry;
+ off_t offset;
+ uint64_t new_possible_extent;
+ uint64_t start_extent, end_extent;
+ uint64_t lb_size, sector, data_length;
+ uint8_t *base;
+ int error, appending, allocated;
+
+ if (!udf_node) return EINVAL;
+
+ if (udf_open_logvol(udf_node->udf_log_vol))
+ return EROFS;
+
+ /* write chanches both modification and file status times */
+ udf_set_timespec_now(&udf_node->stat.st_ctimespec);
+ udf_set_timespec_now(&udf_node->stat.st_mtimespec);
+
+ /* Zero length write -> finished */
+ if (data_uio->uio_resid == 0) return 0;
+
+ /* TODO lock node directly to avoid multiple writers entering */
+/* pthread_rwlock_wrlock(&udf_node->udf_node_lock); */
+
+ if (!udf_node->dirty) {
+ udf_node_mark_dirty(udf_node);
+ }
+
+ /* auto-extent file */
+ appending = 0;
+ allocated = 0;
+ new_possible_extent = data_uio->uio_offset + data_uio->uio_resid;
+ if (new_possible_extent >= (uint64_t) udf_node->stat.st_size) {
+ /* BUGALERT: extra space can be pre-allocated AFTER file length */
+ error = udf_truncate_node(udf_node, new_possible_extent);
+ appending = 1;
+ }
+
+ lb_size = udf_node->udf_log_vol->lb_size;
+ while (data_uio->uio_resid) {
+ error = 0;
+ sector = data_uio->uio_offset / lb_size;
+
+ /* lookup sector in buffer set */
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ udf_lookup_node_buf(udf_node, sector, &buf_entry);
+
+ if (!buf_entry || (buf_entry && (buf_entry->b_lblk != sector))) {
+ /* not found in cache; `page in' sector from file BUT ONLY if we don't completely overwrite it anyway */
+ if ((data_uio->uio_resid < lb_size) && (!appending)) {
+ DEBUG(printf("Reading in file buffer for %s for %"PRIu64" bytes\n", what, (uint64_t) data_uio->uio_resid));
+ error = udf_readin_file_buffer(udf_node, what, sector, content, &buf_entry);
+ }
+
+ /* check if the extent is allocated; check assumption that the size of a buffer is a lb_size */
+ if (buf_entry)
+ assert(buf_entry->b_bufsize == lb_size);
+ UDF_MUTEX_LOCK(&udf_node->alloc_mutex);
+ start_extent = (uint64_t) lb_size * sector;
+ end_extent = MIN((uint64_t) udf_node->stat.st_size, start_extent + lb_size);
+ error = udf_extent_properties(&udf_node->alloc_entries, lb_size, start_extent, end_extent, &allocated);
+ UDF_MUTEX_UNLOCK(&udf_node->alloc_mutex);
+
+ /* check free space if space is to allocated */
+ if (!buf_entry || !allocated) {
+ /* be coulant on metadata here to avoid not to easy to solve resource problems */
+ if (content == UDF_C_USERDATA) {
+ /* check for space no space anymore for userdata (bit on the safe side really) */
+ assert(udf_node->udf_log_vol);
+ if (!udf_confirm_freespace(udf_node->udf_log_vol, content, lb_size)) {
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ return ENOSPC;
+ }
+ }
+ }
+ DEBUG(if (allocated) printf("Writing pre-allocated buffer\n"));
+
+ if (!buf_entry) {
+ /* create new buffer */
+ error = udf_get_buf_entry(udf_node, &buf_entry);
+ if (error) {
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ return error;
+ }
+
+ /* don't forget to set the relative block number! */
+ buf_entry->b_lblk = sector;
+
+ /* add it to the buffer list */
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_attach_buf_to_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+
+ }
+ assert(buf_entry);
+
+ if (allocated) {
+ /*
+ * we could free the old allocated extent and mark it needing allocation
+ * again
+ */
+ }
+ if (!allocated) {
+ /* mark it needs to be allocated, locks are not very nice here */
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_mark_buf_needing_allocate(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ }
+ }
+ if (error) {
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ break; /* while */
+ }
+
+ assert(buf_entry);
+ offset = data_uio->uio_offset - (off_t) sector*lb_size;
+ base = buf_entry->b_data;
+
+ assert(offset >= 0);
+ if (offset >= 0) {
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_mark_buf_dirty(udf_node, buf_entry);
+
+ data_length = buf_entry->b_bufsize - offset;
+ data_length = MIN(data_length, data_uio->uio_resid);
+
+ uiomove(base + offset, data_length, data_uio);
+
+ buf_entry->b_bcount = MAX(buf_entry->b_bcount, offset + data_length);
+ buf_entry->b_resid = buf_entry->b_bufsize - buf_entry->b_bcount;
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ }
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+
+ if (data_uio->uio_resid == 0) return 0; /* finished? */
+ } /* while */
+
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * UDF volume and descriptor logic
+ *
+ ******************************************************************************************/
+
+
+struct udf_volumeset *udf_search_volumeset(char *volset_id) {
+ struct udf_volumeset *volumeset;
+ struct udf_pri_vol *primary;
+
+ /* XXX this is a bit ugly XXX */
+ SLIST_FOREACH(volumeset, &udf_volumeset_list, next_volumeset) {
+ primary = STAILQ_FIRST(&volumeset->primaries);
+ assert(primary->pri_vol);
+ if (memcmp(primary->pri_vol->volset_id, volset_id, 128) == 0) return volumeset;
+ }
+ return NULL;
+}
+
+
+struct udf_pri_vol *udf_search_primary(struct udf_volumeset *set, char *id) {
+ struct udf_pri_vol *primary;
+
+ STAILQ_FOREACH(primary, &set->primaries, next_primary) {
+ assert(primary->pri_vol);
+ if (memcmp(primary->pri_vol->vol_id, id, 32) == 0) return primary;
+ }
+ return NULL;
+}
+
+
+struct udf_log_vol *udf_search_logical_volume_in_primary(struct udf_pri_vol *primary, char *logvol_id) {
+ struct udf_log_vol *here;
+
+ SLIST_FOREACH(here, &primary->log_vols, next_logvol) {
+ if (memcmp(here->log_vol->logvol_id, logvol_id, 128) == 0) return here;
+ }
+ return NULL;
+}
+
+
+
+/* called not very often ... ; free incomming when not needed */
+int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming) {
+ struct udf_volumeset *volset;
+ struct udf_pri_vol *primary;
+
+ assert(current);
+ volset = udf_search_volumeset(incomming->volset_id);
+ if (!volset) {
+ /* create a volume set */
+ volset = calloc(1, sizeof(struct udf_volumeset));
+ if (!volset) {
+ free(incomming);
+ return ENOMEM;
+ }
+
+ /* populate and link in */
+ volset->max_partnum = 0;
+ STAILQ_INIT(&volset->primaries);
+ SLIST_INSERT_HEAD(&udf_volumeset_list, volset, next_volumeset);
+ }
+ assert(volset);
+
+ primary = udf_search_primary(volset, incomming->vol_id);
+ *current = primary;
+
+ if (!primary) {
+ /* create a primary volume */
+ primary = calloc(1, sizeof(struct udf_pri_vol));
+ if (!primary) {
+ free(incomming);
+ return ENOMEM;
+ }
+
+ /* add to volset's primaries list */
+ STAILQ_INSERT_TAIL(&volset->primaries, primary, next_primary);
+
+ *current = primary;
+ } else {
+ /* mark as current */
+
+ /* ok ... we now need to check if this new descriptor is a newer version */
+ if (udf_rw32(incomming->seq_num) <= udf_rw32(primary->pri_vol->seq_num)) {
+ if (udf_session->session_num <= (*current)->udf_session->session_num) {
+ DEBUG(printf("UDF: DISCARDING primary descriptor for its the same but higher session number\n"));
+ /* its an older one; ignore */
+ free(incomming);
+ return 0;
+ }
+ }
+ DEBUG(printf("UPDATING primary descriptor for it has a higher session number\n"));
+ }
+
+ /* update the primary volume descriptor */
+ if (primary->pri_vol) free(primary->pri_vol);
+ primary->volumeset = volset;
+ primary->pri_vol = incomming;
+ primary->udf_session = udf_session;
+
+ return 0;
+}
+
+
+/* not called often ; free lvid when not used */
+int udf_proc_logvol_integrity(struct udf_log_vol *udf_log_vol, struct logvol_int_desc *new_lvid) {
+ struct udf_logvol_info *impl;
+ uint64_t psize, pfree;
+ uint32_t *free_space_pos, *size_pos;
+ uint32_t lb_size, part_map;
+ uint32_t integrity;
+ int error, tagid;
+
+ error = udf_check_tag((union dscrptr *) new_lvid);
+ if (error) {
+ return error; /* return error on faulty tag */
+ }
+
+ tagid = udf_rw16(new_lvid->tag.id);
+ /* getting a terminator tag or zero is an OK condition */
+ if ((tagid == TAGID_TERM) || (tagid == 0)) {
+ return 0;
+ }
+
+ /* not getting an logical volume itegrity volume descriptor is an error now */
+ if (tagid != TAGID_LOGVOL_INTEGRITY) {
+ printf("IEE! got a %d tag while searching for a logical volume integrity descriptor\n", tagid);
+ return EINVAL; /* XXX error code? XXX */
+ }
+
+ /* check CRC on the contents of the logvol integrity */
+ error = udf_check_tag_payload((union dscrptr *) new_lvid);
+ if (error) {
+ return error;
+ }
+
+ /* check logvol integrity validity */
+ integrity = udf_rw32(new_lvid->integrity_type);
+ if ((integrity != UDF_INTEGRITY_OPEN) && (integrity != UDF_INTEGRITY_CLOSED))
+ return EINVAL;
+
+ /* allways go for the next in line; silly but thats Ecma-167/UDF */
+ /* process information contained in logical volume integrity descriptor */
+ udf_log_vol->logvol_state = integrity;
+ udf_log_vol->integrity_serial = udf_rw16(new_lvid->tag.serial_num);
+ impl = (struct udf_logvol_info *) (new_lvid->tables + 2*udf_rw32(new_lvid->num_part));
+
+ udf_log_vol->min_udf_readver = udf_rw16(impl->min_udf_readver);
+ udf_log_vol->min_udf_writever = udf_rw16(impl->min_udf_writever);
+ udf_log_vol->max_udf_writever = udf_rw16(impl->max_udf_writever);
+
+ udf_log_vol->num_files = udf_rw32(impl->num_files);
+ udf_log_vol->num_directories = udf_rw32(impl->num_directories);
+ udf_log_vol->next_unique_id = udf_rw64(new_lvid->lvint_next_unique_id);
+
+ /* calculate free space from this integrity descritor */
+ lb_size = udf_log_vol->lb_size;
+
+ /* init start positions */
+ free_space_pos = &new_lvid->tables[0];
+ size_pos = &new_lvid->tables[udf_log_vol->num_part_mappings];
+
+ /* init counters */
+ udf_log_vol->total_space = udf_log_vol->free_space = udf_log_vol->await_alloc_space = 0;
+ for (part_map = 0; part_map < udf_log_vol->num_part_mappings; part_map++) {
+ psize = udf_rw32(*size_pos); size_pos++;
+ pfree = udf_rw32(*free_space_pos); free_space_pos++;
+ if (pfree != UINT_MAX) {
+ /* if UINT_MAX, its not applicable like virtual space partitions */
+ udf_log_vol->total_space += (uint64_t) psize * lb_size;
+ udf_log_vol->free_space += (uint64_t) pfree * lb_size;
+ }
+ }
+
+ UDF_VERBOSE(
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) {
+ udf_dump_timestamp("\t\t\t\tmarked open at ", &new_lvid->time);
+ } else {
+ udf_dump_timestamp("\t\t\t\tmarked closed at ", &new_lvid->time);
+ }
+ );
+ return 0;
+}
+
+
+void udf_derive_new_logvol_integrity(struct udf_log_vol *udf_log_vol) {
+ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN;
+ udf_log_vol->integrity_serial = 1;
+
+ /* analyse the log vol to check out minimum and maximum read/write versions */
+ if (udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver) == 2) {
+ udf_log_vol->min_udf_readver = 0x0102;
+ udf_log_vol->min_udf_writever = 0x0150;
+ udf_log_vol->max_udf_writever = 0x0150;
+ } else {
+ udf_log_vol->min_udf_readver = 0x0201;
+ udf_log_vol->min_udf_writever = 0x0201;
+ udf_log_vol->max_udf_writever = 0x0201; /* 2.50? */
+ }
+ udf_log_vol->num_files = 0;
+ udf_log_vol->num_directories = 0;
+ udf_log_vol->next_unique_id = 16; /* zero first, rest 15/16+ minimum */
+}
+
+
+int udf_proc_logvol_integrity_sequence(struct udf_log_vol *udf_log_vol) {
+ union dscrptr *dscr;
+ uint32_t sector, length, lvid_len, num_sectors;
+ uint32_t lb_size;
+ int error;
+
+ sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc);
+ length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len);
+ lb_size = udf_log_vol->lb_size;
+
+ /* go for the default `open' integrity first as initialisation */
+ udf_derive_new_logvol_integrity(udf_log_vol);
+
+ if (!length) {
+ fprintf(stderr, "UDF: no volume integrity descriptor sequence space defined... OK for Ecma-167, not for UDF; rejecting\n");
+ return EBADF;
+ }
+
+ error = 0;
+ while (length) {
+ error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len);
+ if (error) {
+ if (dscr) free(dscr);
+ dscr = NULL;
+ break;
+ }
+
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscr));
+
+ error = udf_proc_logvol_integrity(udf_log_vol, &dscr->lvid);
+ if (error) break;
+ if (udf_rw16(dscr->tag.id) == TAGID_TERM) break;
+
+ num_sectors = (lvid_len + lb_size-1) / lb_size;
+ length -= num_sectors * lb_size;
+ sector += num_sectors;
+
+ if (udf_rw32(dscr->lvid.next_extent.len)) {
+ sector = udf_rw32(dscr->lvid.next_extent.loc);
+ length = udf_rw32(dscr->lvid.next_extent.len);
+ }
+ /* free consumed descriptor */
+ free(dscr);
+ dscr = NULL;
+ }
+ /* free dangling descriptor */
+ if (dscr) free(dscr);
+
+ /* either an error has occured or we have processed all descriptors */
+ if (error) {
+ fprintf(stderr, "WARNING: integrity sequence ended with a bad descriptor; creating new\n");
+ udf_derive_new_logvol_integrity(udf_log_vol);
+ return ENOENT;
+ }
+
+ /*
+ * If its marked closed we hope/assume all is fine otherwise it may be
+ * marked closed later on when we are using a VAT and its found and
+ * correct
+ */
+ return 0;
+}
+
+
+/* Add partition mapping to specified logvol descriptor */
+void udf_add_physical_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num) {
+ union udf_pmap *pmap;
+ uint8_t *pmap_pos;
+
+ pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+
+ pmap = (union udf_pmap *) pmap_pos;
+ pmap->pm1.type = 1;
+ pmap->pm1.len = sizeof(struct part_map_1);
+ pmap->pm1.vol_seq_num = udf_rw16(vol_seq_num);
+ pmap->pm1.part_num = udf_rw16(phys_part_num);
+
+ /* increment partition mapping count */
+ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1);
+ logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1));
+
+ logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1));
+}
+
+
+#if 0
+/* not yet */
+void udf_add_sparable_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num, uint16_t packet_len, ) {
+ union udf_pmap *pmap;
+ uint8_t *pmap_pos;
+
+ pmap_pos = logvol->maps + udf_rw32(logvol->mt_l);
+
+ pmap = (union udf_pmap *) pmap_pos;
+ pmap->pm1.type = 1;
+ pmap->pm1.len = sizeof(struct part_map_1);
+ pmap->pm1.vol_seq_num = vol_seq_num;
+ pmap->pm1.part_num = phys_part_num;
+
+ /* increment partition mapping count */
+ logvol->n_pm = udf_rw32(udf_rw32(logvol->n_pm) + 1);
+ logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + sizeof(struct part_map_1));
+
+ logvol->tag.desc_crc_len = udf_rw16(udf_rw16(logvol->tag.desc_crc_len) + sizeof(struct part_map_1));
+}
+#endif
+
+
+/* not called often; free incomming when not needed */
+int udf_proc_log_vol(struct udf_pri_vol *primary, struct udf_log_vol **current, struct logvol_desc *incomming) {
+ struct udf_mountpoint *mp;
+ struct udf_log_vol *logical;
+ struct udf_part_mapping *part_mapping, *data_part_mapping;
+ union udf_pmap *pmap;
+ uint32_t part_cnt, pmap_type, pmap_size;
+ uint32_t data_part_num;
+ uint8_t *pmap_pos;
+
+ logical = udf_search_logical_volume_in_primary(primary, incomming->logvol_id);
+ if (!logical) {
+ /* create a logical volume */
+ logical = calloc(1, sizeof(struct udf_log_vol));
+ if (!logical) {
+ free(incomming);
+ return ENOMEM;
+ }
+
+ /* link in */
+ SLIST_INSERT_HEAD(&primary->log_vols, logical, next_logvol);
+ } else {
+ /* ok ... we now need to check if this new descriptor is a newer version */
+ if (udf_rw32(incomming->seq_num) < udf_rw32(logical->log_vol->seq_num)) {
+ /* its an older one; ignore */
+ free(incomming);
+ return 0;
+ }
+ }
+
+ /* update the logical volume descriptor and its mappings; first delete old partition mappings allocated before */
+ logical->primary = primary;
+ if (current) *current = logical;
+
+ part_mapping = SLIST_FIRST(&logical->part_mappings);
+ while ((part_mapping = SLIST_FIRST(&logical->part_mappings))) {
+ /* TODO cleanup old cruft ? (XXX while mounted? i don't think so!) */
+ /*
+ free(part_mapping->sparing_table);
+ free(part_mapping->vat_file_entry);
+ free(part_mapping->vat);
+ free(part_mapping->meta_file);
+ free(part_mapping->meta_mirror_file);
+ free(part_mapping->meta_bitmap_file);
+ */
+ SLIST_REMOVE_HEAD(&logical->part_mappings, next_mapping);
+ free(part_mapping);
+ }
+ SLIST_INIT(&logical->part_mappings);
+
+ /* use the new logical volume and preprocess it */
+ if (logical->log_vol) free(logical->log_vol);
+ logical->log_vol = incomming;
+ logical->lb_size = udf_rw32(incomming->lb_size);
+ logical->sector_size = primary->udf_session->disc->sector_size;
+
+ /* build up the partion mappings */
+ logical->num_part_mappings = udf_rw32(incomming->n_pm);
+
+ /* process partition mappings */
+ pmap_pos = &logical->log_vol->maps[0];
+ for (part_cnt = 0; part_cnt < logical->num_part_mappings; part_cnt++) {
+ /* get a new part_mapping structure */
+ part_mapping = calloc(1, sizeof(struct udf_part_mapping));
+ assert(part_mapping); /* XXX check with partition mapping destructor etc XXX */
+
+ /* add to list */
+ SLIST_INSERT_HEAD(&logical->part_mappings, part_mapping, next_mapping);
+
+ /* process */
+ pmap = (union udf_pmap *) pmap_pos;
+ pmap_type = pmap->data[0];
+ pmap_size = pmap->data[1];
+
+ part_mapping->udf_virt_part_num = part_cnt;
+ part_mapping->udf_pmap = pmap;
+ switch (pmap_type) {
+ case 1:
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_PHYSICAL;
+ part_mapping->vol_seq_num = udf_rw16(pmap->pm1.vol_seq_num);
+ part_mapping->udf_phys_part_num = udf_rw16(pmap->pm1.part_num);
+ break;
+ case 2:
+ part_mapping->vol_seq_num = udf_rw16(pmap->pm2.vol_seq_num);
+ part_mapping->udf_phys_part_num = udf_rw16(pmap->pm2.part_num);
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Virtual Partition", UDF_REGID_ID_SIZE) == 0) {
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_VIRTUAL;
+ break;
+ }
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Sparable Partition", UDF_REGID_ID_SIZE) == 0) {
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_SPARABLE;
+ break;
+ }
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Metadata Partition", UDF_REGID_ID_SIZE) == 0) {
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_META;
+ break;
+ }
+ printf("HELP ... found unsupported type 2 partition mapping id `%s`; marking broken\n", pmap->pm2.part_id.id);
+ default:
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR;
+ }
+
+ pmap_pos += pmap_size; /* variable length array :( */
+ }
+
+ /* flag all partion mappings data and metadata writable */
+ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) {
+ part_mapping->data_writable = 1;
+ part_mapping->metadata_writable = 1;
+ }
+
+ /* update writable flags depending on mapping type */
+ SLIST_FOREACH(part_mapping, &logical->part_mappings, next_mapping) {
+ switch (part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_ERROR :
+ part_mapping->data_writable = 0;
+ part_mapping->metadata_writable = 0;
+ break;
+ case UDF_PART_MAPPING_PHYSICAL :
+ break;
+ case UDF_PART_MAPPING_VIRTUAL :
+ case UDF_PART_MAPPING_META :
+ /*
+ * These are special in that there is a special metadata partition where no data
+ * is meant to be written on and vice versa
+ */
+
+ /* find the associated data partition */
+ data_part_num = part_mapping->udf_phys_part_num;
+ SLIST_FOREACH(data_part_mapping, &logical->part_mappings, next_mapping) {
+ if (data_part_mapping->udf_phys_part_num == data_part_num) {
+ if (data_part_mapping != part_mapping) {
+ data_part_mapping->metadata_writable = 0;
+ break;
+ }
+ }
+ }
+ part_mapping->data_writable = 0;
+ break;
+ case UDF_PART_MAPPING_SPARABLE :
+ break;
+ }
+ }
+
+ TAILQ_INIT(&logical->dirty_nodes);
+ UDF_MUTEX_INIT(&logical->dirty_nodes_mutex);
+
+ return 0;
+}
+
+
+/* not called often; free incomming when not needed */
+int udf_proc_part(struct udf_pri_vol *primary, struct udf_partition **current, struct part_desc *incomming) {
+ struct udf_partition *udf_partition;
+ struct udf_volumeset *udf_volset;
+ uint32_t new_part_num, sector_size;
+
+ assert(primary);
+ assert(primary->pri_vol);
+
+ udf_volset = udf_search_volumeset(primary->pri_vol->volset_id);
+ assert(udf_volset);
+
+ new_part_num = udf_rw16(incomming->part_num);
+ /* check if its a partition type we recognize */
+ if (strncmp((char *) incomming->contents.id, "+NSR0", 5) != 0) {
+ fprintf(stderr, "Unrecognized partition content type %s encountered; ignoring\n", incomming->contents.id);
+ free(incomming);
+ return 0;
+ }
+
+ /* look if we allready got it */
+
+ SLIST_FOREACH(udf_partition, &udf_volset->parts, next_partition) {
+ if (udf_rw16(udf_partition->partition->part_num) == new_part_num) break;
+ }
+
+ /* we have space... now check if this is a newer one than the one known */
+ if (udf_partition) {
+ if (udf_rw32(incomming->seq_num) < udf_rw32(udf_partition->partition->seq_num)) {
+ /* its an older version */
+ free(incomming);
+ return 0;
+ }
+ } else {
+ /* get us a new udf_partition */
+ udf_partition = calloc(1, sizeof(struct udf_partition));
+ if (!udf_partition) {
+ free(incomming);
+ return ENOMEM;
+ }
+
+ /* link it in */
+ SLIST_INSERT_HEAD(&udf_volset->parts, udf_partition, next_partition);
+ }
+ assert(udf_partition);
+
+ /* copy this new partition descriptor in the list */
+ if (udf_partition->partition) free(udf_partition->partition);
+ udf_partition->partition = incomming;
+ udf_partition->udf_session = primary->udf_session;
+ udf_volset->max_partnum = MAX(udf_volset->max_partnum, new_part_num+1); /* REVIEW why +1? */
+
+ /* initialise */
+ sector_size = primary->udf_session->disc->sector_size;
+ UDF_MUTEX_INIT(&udf_partition->partition_space_mutex);
+ TAILQ_INIT(&udf_partition->unalloc_space_queue);
+ TAILQ_INIT(&udf_partition->freed_space_queue);
+ udf_partition->part_offset = udf_rw32(incomming->start_loc) * sector_size;
+ udf_partition->part_length = udf_rw32(incomming->part_len) * sector_size;
+/* udf_partition->access_type = udf_rw32(incomming->access_type); */
+
+ udf_partition->free_unalloc_space = udf_partition->free_freed_space = 0;
+
+ if (current) *current = udf_partition;
+
+ return 0;
+}
+
+
+/* not called often; free incomming when not needed */
+int udf_proc_filesetdesc(struct udf_log_vol *udf_log_vol, struct fileset_desc *incomming) {
+ struct udf_mountpoint *mp;
+
+ if (udf_rw16(incomming->tag.id) != TAGID_FSD) {
+ printf("IEEE! Encountered a non TAGID_FSD in this fileset descriptor sequence!!!\n");
+ free(incomming);
+ return EFAULT;
+ }
+
+ /* lookup fileset descriptor in this logical volume; interestingly fileset_num is KEY! */
+ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) {
+ if (mp->fileset_desc->fileset_num == incomming->fileset_num) break;
+ }
+
+ if (!mp) {
+ /* add a new mountpoint! */
+ mp = calloc(1, sizeof(struct udf_mountpoint));
+ if (!mp) {
+ free(incomming);
+ return ENOMEM;
+ }
+ mp->fileset_desc = incomming;
+
+ /* insert into udf_log_vol and into mountables list */
+ SLIST_INSERT_HEAD(&udf_log_vol->mountpoints, mp, logvol_next);
+ SLIST_INSERT_HEAD(&udf_mountables, mp, all_next);
+ } else {
+ /* should we update mountpoint? */
+ if (udf_rw32(incomming->fileset_desc_num) <= udf_rw32(mp->fileset_desc->fileset_desc_num)) {
+ /* we allready got a newer one */
+ free(incomming);
+ return 0;
+ }
+
+ fprintf(stderr, "UDF DEBUG: would be updating mountpoint... HELP!\n");
+ /* FIXME delete all inode hash entries */
+ /* XXX how to do that? inodes OK but associated vnodes? XXX */
+#if 0
+ if (!SLIST_EMPTY(&mp->inodes)) {
+ printf("UDF: asked to delete mountpoint with inodes in hashtable!\n");
+ printf("Can't cope with that... aborting\n");
+ exit(1);
+ }
+#endif
+
+ /* free old information (allready in lists though!) */
+ free(mp->fileset_desc);
+ free(mp->mount_name);
+ }
+
+ mp->udf_log_vol = udf_log_vol;
+ mp->fileset_desc = incomming;
+ mp->mount_name = strdup(udf_get_compound_name(mp));
+
+ return 0;
+}
+
+
+int udf_retrieve_volume_space(struct udf_discinfo *disc, struct udf_session *udf_session, struct extent_ad *extent) {
+ struct udf_pri_vol *udf_pri_vol;
+ struct udf_log_vol *udf_log_vol;
+ union dscrptr *dscr;
+ uint32_t sector, length, dscr_len, num_sectors;
+ uint32_t sector_size;
+ int tag_id;
+ int error;
+
+ udf_pri_vol = NULL;
+
+ sector = udf_rw32(extent->loc);
+ length = udf_rw32(extent->len);
+ sector_size = disc->sector_size;
+
+ error = 0; /* XXX zero length area's possible? XXX */
+ while (length) {
+ error = udf_read_session_descriptor(udf_session, sector, "volume descriptor", &dscr, &dscr_len);
+ if (error) {
+ if (dscr) free(dscr);
+ break;
+ }
+
+ tag_id = udf_rw16(dscr->tag.id);
+ num_sectors = (dscr_len + sector_size-1) / sector_size;
+
+ /* proc volume descriptor starting at sector `volume_sector' */
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscr));
+ switch (tag_id) {
+ case TAGID_PRI_VOL :
+ error = udf_proc_pri_vol(udf_session, &udf_pri_vol, &dscr->pvd);
+ break;
+ case TAGID_PARTITION :
+ error = udf_proc_part(udf_pri_vol, NULL, &dscr->pd);
+ break;
+ case TAGID_LOGVOL :
+ error = udf_proc_log_vol(udf_pri_vol, &udf_log_vol, &dscr->lvd);
+ if (!error) {
+ /* first create empty integrity descriptor then modify it on input (for sanity) */
+ udf_derive_new_logvol_integrity(udf_log_vol);
+ }
+ break;
+ case TAGID_TERM :
+ free(dscr);
+ return 0; /* terminator */
+ case TAGID_UNALLOC_SPACE :
+ /* unallocated space descriptor */
+ /* Specifies space that is not claimed yet in partitions (!) */
+ UDF_VERBOSE(printf("\t\t`unallocated space descriptor' ignored\n"));
+ break;
+ case TAGID_IMP_VOL :
+ /* implemenation use volume descriptor */
+ /* Specifies information relevant for the implementator */
+ UDF_VERBOSE_MAX(printf("\t\t`implementation use volume descriptor' ignored\n"));
+ break;
+ case TAGID_VOL :
+ fprintf(stderr, "UDF : untested volume space extender encountered\n");
+ break;
+ default :
+ printf("XXX Unhandled volume sequence %d; freeing\n", tag_id);
+ free(dscr);
+ break;
+ }
+
+ length -= num_sectors * sector_size;
+ sector += num_sectors;
+
+ if (tag_id == TAGID_VOL) {
+ sector = udf_rw32(dscr->vdp.next_vds_ex.loc);
+ length = udf_rw32(dscr->vdp.next_vds_ex.len);
+ free(dscr);
+ }
+ }
+
+ return error;
+}
+
+
+int udf_get_filelength(union dscrptr *dscr, uint64_t *length) {
+ int32_t fe_tag;
+
+ fe_tag = udf_rw16(dscr->tag.id);
+ if (fe_tag == TAGID_FENTRY) {
+ *length = udf_rw64(dscr->fe.inf_len);
+ return 0;
+ } else if (fe_tag == TAGID_EXTFENTRY) {
+ *length = udf_rw64(dscr->efe.inf_len);
+ return 0;
+ }
+ return ENOENT;
+}
+
+
+/* can be passed either a file_entry or an extfil_entry trough fentry! */
+int udf_check_for_vat(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *part_mapping, uint32_t vat_lb, union dscrptr *dscr) {
+ struct udf_part_mapping *s_part_mapping;
+ struct udf_node *vat_udf_node;
+ struct long_ad udf_icbptr;
+ struct regid *regid;
+ struct uio vat_uio;
+ struct iovec vat_iovec;
+ struct icb_tag *icbtag;
+ struct timestamp *mtime;
+ uint64_t vat_length, vat_entries;
+ uint32_t *vat_pos, vpart_num;
+ uint8_t *vat;
+ int error, found;
+
+ /* prepare a `uio' structure for reading in complete VAT file */
+ error = udf_get_filelength(dscr, &vat_length);
+ if (error) return error;
+
+ if (vat_length == 0)
+ return EFAULT;
+
+ vat = malloc(vat_length);
+ if (!vat)
+ return ENOMEM;
+
+ /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */
+ bzero(&vat_uio, sizeof(struct uio));
+ vat_uio.uio_rw = UIO_WRITE; /* WRITE into this space */
+ vat_uio.uio_iovcnt = 1;
+ vat_uio.uio_iov = &vat_iovec;
+ vat_uio.uio_offset = 0; /* begin at the start */
+ vat_uio.uio_resid = vat_length;
+ /* fill in IO vector */
+ vat_uio.uio_iov->iov_base = vat;
+ vat_uio.uio_iov->iov_len = vat_length;
+
+ /* find our virtual partition number corresponding to our physical partition number; this sucks */
+ found = 0;
+ vpart_num = 0;
+ SLIST_FOREACH(s_part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ if (s_part_mapping->udf_phys_part_num == part_mapping->udf_phys_part_num) {
+ if (s_part_mapping->udf_part_mapping_type == UDF_PART_MAPPING_PHYSICAL) {
+ /* found it ! */
+ found = 1;
+ vpart_num = s_part_mapping->udf_virt_part_num;
+ }
+ }
+ }
+ if (!found) {
+ printf("Can't find accompanied physical volume\n");
+ return ENOENT;
+ }
+
+ /* prepare udf_icbptr file node for easy file reading */
+ udf_icbptr.loc.part_num = vpart_num;
+ udf_icbptr.loc.lb_num = udf_rw32(vat_lb);
+ udf_icbptr.len = udf_log_vol->lb_size; /* not used, but may not be zero */
+
+ /*
+ * this udf_node creation and disposing may look a bit inefficient but
+ * its beneficiary for normal file access. its only used once for
+ * reading in the VAT.
+ */
+
+ /* create the udf_vat_node; anonymous since it can't be in a mountpoint */
+ error = udf_readin_anon_udf_node(udf_log_vol, dscr, &udf_icbptr, "VAT", &vat_udf_node);
+ if (!error) {
+ DEBUG(printf("READ FILE PART UIO for VAT\n"));
+ error = udf_read_file_part_uio(vat_udf_node, "VAT contents", 0, &vat_uio);
+ DEBUG(printf("vat_uio rest %d\n", (uint32_t) vat_uio.uio_resid));
+ }
+
+ /* XXX allow for SHORT VAT's ? XXX */
+ if (!error) {
+ if (vat_uio.uio_resid) {
+ fprintf(stderr, "Warning: VAT file can't be read in completely\n");
+ }
+
+ part_mapping->vat_udf_node = vat_udf_node;
+ part_mapping->vat = (struct udf_vat *) vat;
+ part_mapping->vat_length = vat_length;
+
+ /* extract next unique file ID from the VAT file entry's unique ID incremented by one */
+ udf_log_vol->next_unique_id = vat_udf_node->unique_id; /* ok? */
+ udf_increment_unique_id(udf_log_vol);
+
+ /* fentry is confirmed to be either an file_entry or an extfile_entry here */
+ if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
+ icbtag = &dscr->fe.icbtag;
+ mtime = &dscr->fe.mtime;
+
+ } else {
+ icbtag = &dscr->efe.icbtag;
+ mtime = &dscr->efe.mtime;
+ }
+
+ if (icbtag->file_type == UDF_ICB_FILETYPE_VAT) {
+ /* we are in UDF 2.00+ userland */
+ part_mapping->vat_translation = ((uint8_t *) part_mapping->vat) + udf_rw16(part_mapping->vat->header_len);
+ part_mapping->vat_entries = (vat_length - udf_rw16(part_mapping->vat->header_len))/4;
+ udf_log_vol->num_files = udf_rw32(part_mapping->vat->num_files);
+ udf_log_vol->num_directories = udf_rw32(part_mapping->vat->num_directories);
+ udf_log_vol->min_udf_readver = udf_rw16(part_mapping->vat->min_udf_readver);
+ udf_log_vol->min_udf_writever = udf_rw16(part_mapping->vat->min_udf_writever);
+ udf_log_vol->max_udf_writever = udf_rw16(part_mapping->vat->max_udf_writever);
+
+ /* TODO update logvol name */
+ } else {
+ /* still in the old UDF 1.50 userland; update? its notoriously broken */
+ /* check the old UDF 1.50 VAT */
+ DEBUG(printf("CHECK UDF 1.50 VAT\n"));
+ vat_pos = (uint32_t *) vat;
+ vat_entries = (vat_length-36)/4; /* definition */
+
+ regid = (struct regid *) (vat_pos + vat_entries);
+ error = (strncmp((char *) regid->id, "*UDF Virtual Alloc Tbl", 22) == 0) ? 0 : ENOENT;
+ if (!error) {
+ part_mapping->vat_entries = vat_entries;
+ part_mapping->vat_translation = vat;
+ part_mapping->vat = NULL;
+
+ /* num files/dirs? */
+ }
+ }
+ if (!error) {
+ UDF_VERBOSE(udf_dump_timestamp("\t\t\t\tmarked closed at ", mtime));
+ }
+ }
+
+ /* clean up uio structure */
+ if (error) {
+ if (vat) free(vat);
+ if (vat_udf_node) udf_dispose_udf_node(vat_udf_node);
+ part_mapping->vat_udf_node = NULL;
+ }
+
+ return error;
+}
+
+
+int udf_retrieve_supporting_tables(struct udf_log_vol *udf_log_vol) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *part_mapping, *s_part_mapping;
+ struct udf_session *udf_session;
+ struct long_ad udf_icbptr;
+ union dscrptr *possible_vat_fe;
+ union dscrptr *sparing_table_dscr;
+ uint8_t *vat;
+ uint32_t spar_loc;
+ uint64_t first_vat_loc, vat_loc, last_vat_loc;
+ uint32_t vat_part;
+ uint32_t sector_size, lb_size;
+ int part_num, spar_num, data_part_num, vpart_num;
+ int session_num;
+ int error;
+
+ /*
+ * if there are any virtual or sparable partition in this logical
+ * volume, try to find their supporting tables so we can find the rest
+ */
+ lb_size = udf_log_vol->lb_size;
+ sector_size = udf_log_vol->sector_size;
+ SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ part_num = part_mapping->udf_virt_part_num;
+ udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition);
+
+ UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num));
+ switch (part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_ERROR :
+ /* nothing to be done for these */
+ break;
+ case UDF_PART_MAPPING_PHYSICAL :
+ /* nothing to be done for these; no supporting tables */
+ break;
+ case UDF_PART_MAPPING_VIRTUAL :
+ /*
+ * we have to find a good VAT at the END of the session. Since VAT's are
+ * only to be used on WORM's and need to written as last, the strategy is
+ * to go for the predicted end of this session and walk UP
+ */
+ udf_session = udf_log_vol->primary->udf_session;
+ session_num = udf_session->session_num;
+
+ UDF_VERBOSE_TABLES(printf("\t\tSearching for the VAT :\n"));
+ if (udf_session->session_length == 0) {
+ UDF_VERBOSE(
+ printf("\t\tThis virtual partition is inaccessible since its its size is not known;\n");
+ printf("\t\tTry to insert the disc in a CD or DVD recordable device to access it.\n");
+ );
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR;
+ continue;
+ }
+
+ vat = NULL; vat_part = 0;
+ if (udf_session->disc->next_writable[session_num]) {
+ last_vat_loc = udf_session->disc->next_writable[session_num];
+ } else {
+ last_vat_loc = udf_session->disc->session_end[session_num];
+ }
+ last_vat_loc += udf_session->disc->blockingnr;
+
+ /* give some extra slack since sizes are not allways given up correctly */
+ first_vat_loc = last_vat_loc - 256; /* 8 blocks of 32 */
+ first_vat_loc = MAX(first_vat_loc, (uint64_t) udf_session->disc->session_start[session_num]);
+
+ /* try to find the fileid for the VAT; NOTE that we are reading backwards :( */
+ vat_loc = last_vat_loc;
+ do {
+ DEBUG(
+ printf("Trying VAT at sector %d in session\n", (int) vat_loc)
+ );
+ error = udf_read_session_descriptor(udf_session, vat_loc, "VAT file entry", &possible_vat_fe, NULL);
+ if (!error) {
+ error = udf_check_tag_presence(possible_vat_fe, TAGID_FENTRY);
+ if (error)
+ error = udf_check_tag_presence(possible_vat_fe, TAGID_EXTFENTRY);
+ }
+ if (!error) error = udf_check_tag_payload( possible_vat_fe);
+ if (!error) error = udf_check_for_vat(udf_log_vol, part_mapping, vat_loc, possible_vat_fe);
+ if (!error) {
+ break;
+ } else {
+ if (possible_vat_fe) free(possible_vat_fe);
+ vat_loc--;
+ if (vat_loc < first_vat_loc) error = EIO;
+ }
+ } while (error != EIO);
+
+ if (error) {
+ printf("WARNING: was looking for a VAT but didnt find it; marking logical volume broken\n");
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR;
+ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN;
+ udf_log_vol->broken = 1;
+ continue;
+ }
+ UDF_VERBOSE_TABLES(printf("\t\t\tfound VAT file-entry at device logical sector %d\n", (uint32_t) vat_loc));
+ UDF_VERBOSE_TABLES(printf("\t\t\tFound %d byte VAT descriptor+table\n", (uint32_t) part_mapping->vat_length));
+
+ UDF_VERBOSE_TABLES(udf_dump_descriptor(possible_vat_fe));
+ UDF_VERBOSE_MAX(udf_dump_vat_table(part_mapping));
+
+ if (part_mapping->vat_translation) {
+ /* the presence of a correct VAT means the logical volume is in a closed state */
+ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED;
+ UDF_VERBOSE(printf("\t\t\t\tmarked closed due to presence of VAT\n"));
+
+ /* XXX update `free' space by requesting the device's free space? XXX */
+ udf_log_vol->free_space = udf_partition->part_offset + udf_partition->part_length - vat_loc*sector_size;
+ }
+
+ if (!udf_session->disc->sequential) {
+ UDF_VERBOSE(printf("\t\t\t\tenabling sequential media emulation\n"));
+ udf_session->disc->sequential = 1;
+ }
+ break;
+ case UDF_PART_MAPPING_SPARABLE :
+ /* we have to find a good sparing table; address are in device logical blocks */
+ udf_session = udf_log_vol->primary->udf_session;
+
+ for(spar_num = 0; spar_num < part_mapping->udf_pmap->pms.n_st; spar_num++) {
+ spar_loc = udf_rw32(part_mapping->udf_pmap->pms.st_loc[spar_num]);
+
+ /* fetch spar_loc's table ; on THIS session. */
+ error = udf_read_session_descriptor(udf_session, spar_loc, "Sparing table", &sparing_table_dscr, NULL);
+ if (!error) error = udf_check_tag_presence(sparing_table_dscr, TAGID_SPARING_TABLE);
+ if (!error) {
+ UDF_VERBOSE_TABLES(printf("\t\tFound the sparing table\n"));
+ part_mapping->sparing_table = &sparing_table_dscr->spt;
+ break;
+ } else {
+ if (sparing_table_dscr) free(sparing_table_dscr);
+ }
+ }
+ if (!part_mapping->sparing_table) {
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR;
+ }
+ UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->sparing_table));
+ break;
+ case UDF_PART_MAPPING_META :
+ /*
+ * set up common locator parts; the files are located inside the `part_num' partion where
+ * this partition is a added layer on.
+ */
+
+ /* find the associated data partition */
+ data_part_num = udf_rw16(part_mapping->udf_pmap->pmm.part_num);
+
+ /* find our virtual partition number corresponding to our physical partition number; this sucks */
+ vpart_num = data_part_num;
+ SLIST_FOREACH(s_part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ if (s_part_mapping->udf_phys_part_num == data_part_num) {
+ if (s_part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_META) {
+ /* found it ! */
+ vpart_num = s_part_mapping->udf_virt_part_num;
+ break;
+ }
+ }
+ }
+ udf_icbptr.loc.part_num = vpart_num;
+ udf_icbptr.len = lb_size; /* defined as maximum size */
+
+ UDF_VERBOSE_TABLES(printf("Reading metadata partition filedescriptor\n"));
+ udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_file_lbn);
+ error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition file descriptor", &part_mapping->meta_file);
+ if (error == 0)
+ UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_file));
+
+ udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_mirror_file_lbn);
+ if ((error == 0) && (udf_icbptr.loc.lb_num != (uint32_t) -1)) {
+ UDF_VERBOSE_TABLES(printf("Reading metadata partition mirror filedescriptor\n"));
+ error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition mirror file descriptor", &part_mapping->meta_mirror_file);
+ if (error == 0)
+ UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_mirror_file));
+error = 0; /* XXX ignoring error code for now */
+ }
+
+ udf_icbptr.loc.lb_num = udf_rw32(part_mapping->udf_pmap->pmm.meta_bitmap_file_lbn);
+ if ((error == 0) && (udf_icbptr.loc.lb_num != (uint32_t) -1)) {
+ UDF_VERBOSE_TABLES(printf("Reading metadata partition bitmap filedescriptor\n"));
+ error = udf_readin_anon_udf_node(udf_log_vol, NULL, &udf_icbptr, "Metadata partition bitmap file descriptor", &part_mapping->meta_bitmap_file);
+ if (error == 0)
+ UDF_VERBOSE_TABLES(udf_dump_descriptor((union dscrptr *) part_mapping->meta_bitmap_file));
+ }
+
+ /* if something is wrong, then mark it as a broken partition */
+ if (error) {
+ /* TODO handle read-errors on the meta data and meta data mirror file descriptors. */
+ part_mapping->udf_part_mapping_type = UDF_PART_MAPPING_ERROR;
+ }
+ break;
+ }
+ }
+ UDF_VERBOSE_TABLES(printf("\n"));
+ if (udf_log_vol->broken) return EIO;
+
+ return 0;
+}
+
+
+int udf_retrieve_space_tables(struct udf_log_vol *udf_log_vol) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *part_mapping;
+ struct part_hdr_desc *part_hdr_desc;
+ union dscrptr *dscrptr;
+ uint32_t sector;
+ uint32_t lb_size;
+ uint64_t length;
+ int vpart_num, ppart_num;
+ int error;
+
+ lb_size = udf_log_vol->lb_size;
+ SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ vpart_num = part_mapping->udf_virt_part_num;
+ ppart_num = part_mapping->udf_phys_part_num;
+ UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", vpart_num, ppart_num));
+
+ if ((part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_PHYSICAL) &&
+ (part_mapping->udf_part_mapping_type != UDF_PART_MAPPING_SPARABLE)) {
+ UDF_VERBOSE_TABLES(printf("\t\tDon't know how to load space tables for type %d\n", part_mapping->udf_part_mapping_type));
+ continue;
+ }
+
+ /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, NULL, &udf_partition);
+ assert(udf_partition);
+ part_hdr_desc = &udf_partition->partition->pd_part_hdr;
+
+ sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num);
+ length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */
+ if (length) {
+ error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Unallocated space table", &dscrptr, NULL);
+ UDF_VERBOSE_MAX(printf("\tUnalloced space table\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ /* udf_process_space_table(&udf_partition->unalloc_space, dscrptr); */
+ free(dscrptr);
+ }
+
+ sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num);
+ length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len);
+ if (length && (udf_partition->unalloc_space_bitmap == 0)) {
+ error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Unallocated space bitmap", &dscrptr, NULL);
+ if (!error) {
+ UDF_VERBOSE_MAX(printf("\tUnalloced space bitmap\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ udf_read_in_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_unalloc_space);
+ UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's unallocated space : %"PRIu64"\n", udf_partition->free_unalloc_space));
+ udf_partition->unalloc_space_bitmap = &dscrptr->sbd;
+ } else {
+ printf("While reading in unallocated space bitmap : %s\n", strerror(error));
+ udf_partition->unalloc_space_bitmap = NULL;
+ /* TODO mark read-only logvol */
+ }
+ }
+
+ sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num);
+ length = udf_rw32(part_hdr_desc->freed_space_table.len);
+ if (length) {
+ error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Freed space table", &dscrptr, NULL);
+ UDF_VERBOSE_MAX(printf("\tFreed space table\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ /* udf_process_space_table(&udf_partition->freed_space, dscrptr); */
+ free(dscrptr);
+ }
+
+ sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num);
+ length = udf_rw32(part_hdr_desc->freed_space_bitmap.len);
+ if (length && (udf_partition->freed_space_bitmap == NULL)) {
+ error = udf_read_logvol_descriptor(udf_log_vol, vpart_num, sector, "Freed space bitmap", &dscrptr, NULL);
+ if (!error) {
+ UDF_VERBOSE_MAX(printf("\tFreed space bitmap\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ udf_read_in_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size, &udf_partition->free_freed_space);
+ UDF_VERBOSE_TABLES(printf("\t\tPhysical partition's freed space : %"PRIu64"\n", udf_partition->free_unalloc_space));
+ udf_partition->freed_space_bitmap = &dscrptr->sbd;
+ } else {
+ printf("While reading in freed space bitmap : %s\n", strerror(error));
+ udf_partition->freed_space_bitmap = NULL;
+ /* TODO mark read-only logvol */
+ }
+ }
+ }
+ UDF_VERBOSE_TABLES(printf("\n"));
+
+ return 0;
+}
+
+
+/*
+ * Fileset descriptors are on a logical volume's partitions; since virtual
+ * partitions are then also possible its OK to use the VAT for redefining the
+ * fileset descriptors.
+ */
+int udf_retrieve_fileset_descriptor(struct udf_log_vol *udf_log_vol) {
+ struct udf_mountpoint *mountable;
+ struct long_ad *fsd_loc;
+ struct fileset_desc *new_fsd;
+ struct udf_node *vnode;
+ uint32_t part_num, lb_num, length;
+ int32_t error;
+
+ error = 0; /* flag OK */
+
+ fsd_loc = &udf_log_vol->log_vol->_lvd_use.fsd_loc;
+ part_num = udf_rw16(fsd_loc->loc.part_num);
+ lb_num = udf_rw32(fsd_loc->loc.lb_num);
+ length = udf_rw32(fsd_loc->len);
+
+ while (length && !error) {
+ UDF_VERBOSE_TABLES(
+ printf("\tFileset descriptor extent at sector %d within partion %d for %d bytes\n", lb_num, part_num, length)
+ );
+
+ /* only go for ONE fsb at a time */
+ error = udf_read_logvol_descriptor(udf_log_vol, part_num, lb_num, "Fileset descriptor", (union dscrptr **) &new_fsd, NULL);
+ if (!error) error = udf_check_tag((union dscrptr *) new_fsd);
+
+ /* TODO need a clearer handling unrecorded blocks here */
+ if (error || (!new_fsd) || (new_fsd && (udf_rw16(new_fsd->tag.id) == TAGID_TERM))) {
+ /* end of sequence */
+ UDF_VERBOSE_TABLES(
+ printf("\t\t(Terminator) ");
+ if (!new_fsd || error) printf("; unrecorded"); else printf("; explicit");
+ printf("\n");
+ );
+ /* clear error to indicate end of sequence and free possible read in descriptor */
+ error = 0;
+ if (new_fsd) free(new_fsd);
+ break;
+ }
+
+ UDF_VERBOSE_MAX(udf_dump_descriptor((union dscrptr *) new_fsd));
+ udf_proc_filesetdesc(udf_log_vol, new_fsd);
+
+ if (udf_rw32(new_fsd->next_ex.len) == 0) {
+ /* next entry */
+ lb_num += 1;
+ length -= udf_log_vol->lb_size;
+ } else {
+ /* follow the next extent */
+ fsd_loc = &new_fsd->next_ex;
+ part_num = udf_rw16(fsd_loc->loc.part_num);
+ lb_num = udf_rw32(fsd_loc->loc.lb_num);
+ length = udf_rw32(fsd_loc->len);
+ }
+ }
+ UDF_VERBOSE_TABLES(printf("\n"));
+
+ if (error) return error;
+
+ /* if no error occured, create rootdir udf_nodes */
+ SLIST_FOREACH(mountable, &udf_log_vol->mountpoints, logvol_next) {
+ /* errors are OK */
+ udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->rootdir_icb, "Rootdir", &mountable->rootdir_node);
+ udf_readin_anon_udf_node(udf_log_vol, NULL, &mountable->fileset_desc->streamdir_icb, "Streamdir", &mountable->streamdir_node);
+
+ /* keep names the same ? (duplicate code ahead ... ) */
+ if (mountable->rootdir_node) {
+ vnode = mountable->rootdir_node;
+
+ vnode->mountpoint = mountable;
+ vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX;
+ vnode->stat.st_mode = 0777 | S_IFDIR;
+
+ udf_insert_node_in_hash(vnode);
+ }
+ if (mountable->streamdir_node) {
+ vnode = mountable->streamdir_node;
+
+ vnode->mountpoint = mountable;
+ vnode->stat.st_uid = vnode->stat.st_gid = UINT_MAX;
+ vnode->stat.st_mode = 0777 | S_IFDIR;
+
+ udf_insert_node_in_hash(vnode);
+ }
+ }
+
+ return 0;
+}
+
+
+int udf_check_writable_filesets(struct udf_log_vol *udf_log_vol, int mnt_flags) {
+ struct udf_mountpoint *mp;
+ struct udf_part_mapping *udf_part_mapping;
+ int writable;
+
+ writable = 1;
+ if (mnt_flags & UDF_MNT_RDONLY)
+ writable = 0;
+
+ if (mnt_flags & UDF_MNT_FORCE)
+ writable = 1;
+
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) {
+ if (!(mnt_flags & UDF_MNT_FORCE)) {
+ /* we explicitly DISABLE writing */
+ /* XXX do we even reach here? XXX */
+ if (udf_verbose) {
+ printf("\t\t\t\tmounting READ-ONLY due to open integrity\n");
+ } else {
+ printf("WARNING: mounting logical volume READ-ONLY due to open integrity\n");
+ }
+ writable = 0;
+ } else {
+ printf("WARNING: ignoring open integrity\n");
+ }
+ }
+
+ SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ if (udf_part_mapping->udf_part_mapping_type == UDF_PART_MAPPING_META) {
+ writable = 0;
+ fprintf(stderr, "\t\t\t\t*** marked read-only due to read-only support for Metadata partition ***\n");
+ }
+ }
+
+ /* follow all mountpoints of this logical volume and set if they are writable */
+ SLIST_FOREACH(mp, &udf_log_vol->mountpoints, logvol_next) {
+ mp->writable = writable;
+ }
+ udf_log_vol->writable = writable;
+
+ /* WAS: */
+ /* udf_log_vol->primary->udf_session->writable = mark; */
+ return 0;
+}
+
+
+/*
+ * udf_eliminate_predescessor_volumesets()
+ *
+ * We are faced with a curious problem : we are to examine the partitions and
+ * determine which are successors of eachother. This is propably most
+ * relevant only on WORM media though. We could consider the following rules :
+ * 1) `glue' according to strict UDF level 1 rules ?
+ * 2) use heuristics info i.e. NERO, DirectCD and mkisofs quirks ?
+ * 3) use overlapping partitions to detect relationships ?
+ *
+ * Using 1 would imply no multi-volume discs and thus glue everything but that
+ * could easily be wrong. Selecting by volumeset names is not possible for
+ * Nero f.e. just creates random volumeset names every session and uses no
+ * volume version information and thus also violates the UDF rules.
+ *
+ * Using 2 would be tricky; we know a few programs but what if more are
+ * developped? We then would be at loss.
+ *
+ * Using 3 would imply some calculation but is fail-safe in both supporting
+ * multiple volumes on one disc (they seperate) and in supporting
+ * multi-session WORM media for these will refer to eachother. Offcource NERO
+ * could faul this by just extending the zero partion to the whole disc in its
+ * ignorance and thus create false overlapping over other independent
+ * sessions. This is to be investigated. I don't know how NERO will react on
+ * this situation.
+ *
+ * Propably method 3 would be good to try :
+ *
+ * Follow the disc and check for all sessions in order to mark the ones with
+ * overlapping partitions as `inactive' and keep the latest one active.
+ * Sessions with the `local' quirk are seperate allmost by default; should
+ * be change the offsets? would not be too difficult but possible.
+ */
+
+void udf_eliminate_predescessor_volumesets(struct udf_discinfo *disc) {
+ struct udf_volumeset *anc_vol_set;
+ struct udf_volumeset *sib_vol_set;
+ struct pri_vol_desc *anc_pri_vol;
+ struct pri_vol_desc *sib_pri_vol;
+ struct udf_partition *anc_part;
+ struct udf_partition *sib_part;
+ int anc_partnum;
+ int sib_partnum;
+ uint32_t anc_start, anc_end;
+ uint32_t sib_start, sib_end;
+ uint32_t overlap_start, overlap_end;
+ uint32_t anc_session;
+ uint32_t sib_session;
+
+ SLIST_FOREACH(anc_vol_set, &udf_volumeset_list, next_volumeset) {
+ anc_pri_vol = STAILQ_FIRST(&anc_vol_set->primaries)->pri_vol;
+ sib_vol_set = SLIST_NEXT(anc_vol_set, next_volumeset);
+ while (sib_vol_set) {
+ sib_pri_vol = STAILQ_FIRST(&sib_vol_set->primaries)->pri_vol;
+ DEBUG(
+ printf("checking volset %s with volset %s\n", anc_pri_vol->volset_id+1, sib_pri_vol->volset_id+1)
+ );
+ /* compare these two volume sets but only process partitions on _this_ disc */
+ SLIST_FOREACH(anc_part, &anc_vol_set->parts, next_partition) {
+ if (anc_part->udf_session->disc != disc) continue;
+
+ anc_session = anc_part->udf_session->session_num;
+ anc_start = 0;
+#if 0
+ if (disc->session_quirks[anc_session] & CD_SESS_QUIRK_SESSION_LOCAL)
+ anc_start += disc->session_start[anc_session];
+#endif
+ anc_start += udf_rw32(anc_part->partition->start_loc);
+ anc_end = anc_start + udf_rw32(anc_part->partition->part_len);
+
+ SLIST_FOREACH(sib_part, &sib_vol_set->parts, next_partition) {
+ if (sib_part->udf_session->disc != disc) continue;
+
+ sib_session = sib_part->udf_session->session_num;
+#if 0
+ /* can `session local' volumes even be considered part/successor ? */
+ if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL) continue;
+#endif
+ sib_start = 0;
+#if 0
+ if (disc->session_quirks[sib_session] & CD_SESS_QUIRK_SESSION_LOCAL)
+ sib_start += disc->session_start[sib_session];
+#endif
+ sib_start += udf_rw32(sib_part->partition->start_loc);
+ sib_end = sib_start + udf_rw32(sib_part->partition->part_len);
+DEBUG(
+anc_partnum = udf_rw16(anc_part->partition->part_num);
+sib_partnum = udf_rw16(sib_part->partition->part_num);
+printf("\t\tchecking partition %d with partition %d ([%d-%d] x [%d-%d])\n", anc_partnum, sib_partnum, anc_start, anc_end, sib_start, sib_end)
+);
+ overlap_start = MAX(sib_start, anc_start);
+ overlap_end = MIN(sib_end, sib_end);
+ if (overlap_start < overlap_end) {
+DEBUG(
+printf("\t\t\tOVERLAP!\n")
+);
+ if (sib_session < anc_session) {
+ /* the sibbling is older */
+ UDF_VERBOSE_TABLES(
+ printf("\tVolume set ");
+ udf_dump_id(NULL, 128, anc_pri_vol->vol_id, &anc_pri_vol->desc_charset);
+ printf(" is a newer version of volume set ");
+ udf_dump_id(NULL, 128, sib_pri_vol->vol_id, &sib_pri_vol->desc_charset);
+ printf("\n");
+ );
+ sib_vol_set->obsolete = 1;
+ break;
+ }
+ } /* overlap */
+ if (sib_vol_set->obsolete) break;
+ } /* sibling partition */
+ if (sib_vol_set->obsolete) break;
+ } /* ancestor partition */
+ sib_vol_set = SLIST_NEXT(sib_vol_set, next_volumeset);
+ } /* sibling volume set */
+ } /* ancestor volume set */
+}
+
+
+int udf_add_session_to_discinfo(struct udf_discinfo *disc, int session, struct anchor_vdp *avdp, int error) {
+ struct udf_session *udf_session;
+
+ udf_session = calloc(1, sizeof(struct udf_session));
+ assert(udf_session);
+
+ if (!error) {
+ memcpy(&udf_session->anchor, avdp, sizeof(struct anchor_vdp));
+ }
+
+ udf_session->disc = disc;
+ udf_session->session_num = session;
+ udf_session->session_offset = 0;
+ udf_session->session_length = disc->session_end[session] - disc->session_start[session];
+ disc->session_quirks[session] = 0;
+
+ /* writable session administration */
+ udf_session->writable = 0; /* default off */
+ error = udf_init_session_caches(udf_session);
+
+ if (!error) {
+ /* detect quirks */
+ /* XXX session local disabled due to wrong heuristic XXX */
+#if 0
+ if (disc->session_start[session] > 0) {
+ if ((udf_session->anchor.main_vds_ex.loc < disc->session_start[session])) {
+ disc->session_quirks[session] |= CD_SESS_QUIRK_SESSION_LOCAL;
+ udf_session->session_offset = disc->session_start[session];
+ }
+ }
+#endif
+ }
+
+ /* add to tail of session list */
+ STAILQ_INSERT_TAIL(&disc->sessions, udf_session, next_session);
+
+ disc->num_udf_sessions++;
+
+ /* record status of this volume */
+ disc->session_is_UDF[session] = error ? 0 : 1;
+
+ return error;
+}
+
+
+int udf_get_anchors(struct udf_discinfo *disc) {
+ uint8_t *sector;
+ union dscrptr *dscr;
+ uint32_t session_start, session_end;
+ int session, error;
+
+ /* Get all anchors */
+ STAILQ_INIT(&disc->sessions);
+
+ sector = NULL;
+ for (session = 0; session < disc->num_sessions; session++) {
+ /* check for anchors ; no volume recognition data ? */
+ session_start = disc->session_start[session];
+ session_end = disc->session_end [session]-1;
+
+ sector = calloc(1, disc->sector_size);
+ if (!sector) return ENOMEM;
+
+ dscr = (union dscrptr *) sector;
+ error = udf_read_physical_sectors(disc, session_end, 1, "Anchor", sector);
+ if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR);
+ if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end (%d)\n", session_end));
+ if (error) {
+ error = udf_read_physical_sectors(disc, session_end - 256, 1, "Anchor", sector);
+ if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR);
+ if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session end - 256 (%d)\n", session_end - 256));
+ if (error) {
+ error = udf_read_physical_sectors(disc, session_start + 256, 1, "Anchor", sector);
+ if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR);
+ if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 256 (%d)\n", session_start + 256));
+ if (error) {
+ /* unclosed CD recordable case due to track reservation for iso9660 filesystems */
+ error = udf_read_physical_sectors(disc, session_start + 512, 1, "Anchor", sector);
+ if (!error) error = udf_check_tag_presence(dscr, TAGID_ANCHOR);
+ if (!error) UDF_VERBOSE_TABLES(printf("Accepting anchor at session sector 512 (%d)\n", session_start + 512));
+ }
+ }
+ }
+
+ if (!error) {
+ udf_add_session_to_discinfo(disc, session, (struct anchor_vdp *) sector, error);
+ } else {
+ free(sector);
+ }
+ }
+
+ return 0;
+}
+
+
+int udf_get_volumeset_space(struct udf_discinfo *disc) {
+ struct udf_session *udf_session;
+ int one_good_found;
+ int error;
+
+ /* Rip all volume spaces */
+ one_good_found = 0;
+ UDF_VERBOSE(printf("\tretrieving volume space\n"));
+ STAILQ_FOREACH(udf_session, &disc->sessions, next_session) {
+ UDF_VERBOSE_MAX(printf("Session %d volumes : \n", udf_session->session_num));
+
+ error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.main_vds_ex);
+ if (error) {
+ printf("\nError retrieving session %d's volume space; prosessing reserve\n", udf_session->session_num);
+ error = udf_retrieve_volume_space(disc, udf_session, &udf_session->anchor.reserve_vds_ex);
+ }
+ if (!error)
+ one_good_found = 1;
+ }
+
+ return one_good_found ? 0 : ENOENT;
+}
+
+
+int udf_get_logical_volumes_supporting_tables(struct udf_discinfo *disc, int mnt_flags) {
+ struct udf_volumeset *udf_volumeset;
+ struct udf_pri_vol *udf_pri_vol;
+ struct udf_log_vol *udf_log_vol;
+ int logvolint_error;
+ int one_good_found;
+ int error;
+
+ one_good_found = 0;
+ SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) {
+ if (!udf_volumeset->obsolete) {
+ STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) {
+ if (udf_pri_vol->udf_session->disc == disc) {
+ SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) {
+ /* retrieving logical volume integrity sequence */
+ UDF_VERBOSE(udf_dump_volume_name("\t\tLogical volume ", udf_log_vol));
+ UDF_VERBOSE(printf("\t\t\tintegrity\n"));
+ logvolint_error = udf_proc_logvol_integrity_sequence(udf_log_vol);
+
+ /* load in supporting tables */
+ UDF_VERBOSE(printf("\t\t\tsupporting tables\n"));
+ error = udf_retrieve_supporting_tables(udf_log_vol);
+
+ /* if the state is still marked `open', its dirty and we mount read-only for safety */
+ if (logvolint_error) {
+ printf("\t\t\t*** marked read-only due to logvol integrity error ***\n");
+ mnt_flags |= UDF_MNT_RDONLY;
+ }
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN) {
+ printf("\t\t\t*** marked read-only due to open logical volume ***\n");
+ mnt_flags |= UDF_MNT_RDONLY;
+ }
+
+ /* get fileset descriptors */
+ UDF_VERBOSE(printf("\t\t\tfileset(s)\n"));
+ if (!error) error = udf_retrieve_fileset_descriptor(udf_log_vol);
+
+ /* check if the logical volume is writable */
+ UDF_VERBOSE(printf("\t\t\tchecking writable filesets\n"));
+ if (!error) error = udf_check_writable_filesets(udf_log_vol, mnt_flags);
+
+ /* load in free/used space tables for writable volsets */
+ UDF_VERBOSE(printf("\t\t\tused/freed space tables\n"));
+ if (!error) error = udf_retrieve_space_tables(udf_log_vol);
+
+ if (error) {
+ udf_log_vol->broken = 1;
+ } else {
+ one_good_found = 1;
+ }
+ } /* logical */
+ } /* disc */
+ } /* primary */
+ } /* if */
+ } /* volumeset */
+
+ return one_good_found? 0 : ENOENT;
+}
+
+
+/******************************************************************************************
+ *
+ * Disc sync
+ *
+ ******************************************************************************************/
+
+
+void udf_sync_tables_callback(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata) {
+ /* struct udf_node *udf_node = (struct udf_node *) wrcallback->structure; */
+
+ wrcallback = wrcallback; /* not used now */
+ sectordata = sectordata;
+
+ if (reason == UDF_WRCALLBACK_REASON_PENDING) {
+ /* what to do? */
+ return;
+ }
+ if (reason == UDF_WRCALLBACK_REASON_ANULATE) {
+ /* what to do? */
+ return;
+ }
+ assert(reason == UDF_WRCALLBACK_REASON_WRITTEN);
+ if (error) {
+ printf("UDF error: sync tables write errors in syncnode not fixed!\n");
+ return;
+ }
+}
+
+
+/* TODO space tables are not coupled on a logical volume but on a partition/disc, so call them on that instead of logvol */
+int udf_sync_space_tables(struct udf_log_vol *udf_log_vol) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *part_mapping;
+ struct part_hdr_desc *part_hdr_desc;
+ struct udf_wrcallback wr_callback;
+ union dscrptr *dscrptr;
+ uint64_t length;
+ uint32_t sector;
+ uint32_t lb_size, part_start, part_len;
+ uint16_t dscr_ver;
+ int part_num;
+ int error;
+
+ lb_size = udf_log_vol->lb_size;
+
+ wr_callback.function = udf_sync_tables_callback;
+ SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ part_num = part_mapping->udf_virt_part_num;
+ UDF_VERBOSE_TABLES(printf("\tFor partition mapping %d->%d\n", part_num, part_mapping->udf_phys_part_num));
+
+ /* retrieve and process unallocated- and freed-space information for all used partitions of the logvol */
+ error = udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition);
+ assert(udf_partition);
+
+ part_hdr_desc = &udf_partition->partition->pd_part_hdr;
+ part_start = udf_rw32(udf_partition->partition->start_loc);
+ part_len = udf_rw32(udf_partition->partition->part_len);
+ dscr_ver = udf_rw16(udf_partition->partition->tag.descriptor_ver);
+
+ sector = udf_rw32(part_hdr_desc->unalloc_space_table.lb_num);
+ length = udf_rw32(part_hdr_desc->unalloc_space_table.len); /* needed? */
+ if (length) {
+ printf("UDF: Can't write space tables yet\n");
+#if 0
+ error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Unallocated space table", &dscrptr, NULL);
+ UDF_VERBOSE_MAX(printf("\tUnalloced space table\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ //udf_process_space_table(&udf_partition->unalloc_space, dscrptr);
+ free(dscrptr);
+#endif
+ }
+
+ sector = udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num);
+ length = udf_rw32(part_hdr_desc->unalloc_space_bitmap.len);
+ /* printf("unalloc dscr at partition sector %d\n", sector); */
+ if (length) {
+ /* read it in and modify */
+ dscrptr = (union dscrptr *) udf_partition->unalloc_space_bitmap;
+ if (!dscrptr) {
+ printf("Warning: creating empty unallocated space bitmap for partition's is gone\n");
+ error = udf_create_empty_space_bitmap(lb_size, dscr_ver, /* num_lbs */ part_len, (struct space_bitmap_desc **) &dscrptr);
+ assert(!error);
+ assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length);
+ udf_partition->unalloc_space_bitmap = &dscrptr->sbd;
+ }
+
+ udf_sync_space_bitmap(&udf_partition->unalloc_space_queue, &dscrptr->sbd, lb_size);
+ UDF_VERBOSE_MAX(printf("\tWriteout unallocated space bitmap\n"));
+ UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr));
+ udf_write_partition_descriptor(udf_partition, sector, "Unallocated space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */
+ }
+
+ sector = udf_rw32(part_hdr_desc->freed_space_table.lb_num);
+ length = udf_rw32(part_hdr_desc->freed_space_table.len);
+ if (length) {
+ printf("UDF: Can't write space tables yet\n");
+#if 0
+ error = udf_read_logvol_descriptor(udf_log_vol, part_num, sector, "Freed space table", &dscrptr, NULL);
+ UDF_VERBOSE_MAX(printf("\tFreed space table\n"));
+ UDF_VERBOSE_MAX(udf_dump_descriptor(dscrptr));
+ //udf_process_space_table(&udf_partition->freed_space, dscrptr);
+ free(dscrptr);
+#endif
+ }
+
+ sector = udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num);
+ length = udf_rw32(part_hdr_desc->freed_space_bitmap.len);
+/* printf("freed dscr at partition sector %d\n", sector); */
+ if (length) {
+ /* read it in and modify */
+ dscrptr = (union dscrptr *) udf_partition->freed_space_bitmap;
+ if (!dscrptr) {
+ printf("Warning: creating empty freed space bitmap for partition's is gone\n");
+ error = udf_create_empty_space_bitmap(lb_size, dscr_ver, part_len, (struct space_bitmap_desc **) &dscrptr);
+ assert(!error);
+ assert(udf_calc_tag_malloc_size(dscrptr, lb_size) <= length);
+ udf_partition->freed_space_bitmap = &dscrptr->sbd;
+ }
+
+ udf_sync_space_bitmap(&udf_partition->freed_space_queue, &dscrptr->sbd, lb_size);
+ UDF_VERBOSE_MAX(printf("\tWriteout freed space bitmap\n"));
+ UDF_VERBOSE_MAX(udf_validate_tag_and_crc_sums((union dscrptr *) dscrptr); udf_dump_descriptor(dscrptr));
+ udf_write_partition_descriptor(udf_partition, sector, "Freed space bitmap", dscrptr, &wr_callback); /* SESSION descriptor!! */
+ }
+ }
+ UDF_VERBOSE_TABLES(printf("\n"));
+
+ return 0;
+}
+
+
+int udf_writeout_LVID(struct udf_log_vol *udf_log_vol, int type) {
+ union dscrptr *dscr;
+ struct logvol_int_desc *intdesc;
+ struct udf_logvol_info *impl;
+ struct udf_session *session;
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *part_mapping;
+ struct desc_tag *terminator;
+ struct udf_wrcallback wr_callback;
+ uint32_t sector, lvid_sector, term_sector;
+ uint32_t part_num, *free_space_pos, *size_pos, lb_size;
+ uint32_t len, length, lvid_len, num_sectors;
+ int error, dscr_ver, tagid;
+
+ /* create a new `fresh' logvol integrity */
+ session = udf_log_vol->primary->udf_session;
+ lb_size = udf_log_vol->lb_size;
+ num_sectors = lb_size / session->disc->sector_size;
+
+ intdesc = calloc(1, udf_log_vol->lb_size);
+ if (!intdesc)
+ return ENOMEM;
+
+ sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc);
+ length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len);
+
+ if (!length)
+ return ENOENT;
+
+ /* search insertion place */
+ lvid_sector = 0;
+ term_sector = 0;
+ while (length) {
+ error = udf_read_session_descriptor(udf_log_vol->primary->udf_session, sector, "Logical volume integrity descriptor (LVID)", &dscr, &lvid_len);
+
+ /* getting a terminator tag or zero is an OK condition */
+ if (error) {
+ tagid = 0;
+ } else {
+ tagid = udf_rw16(dscr->tag.id);
+ }
+ if ((tagid == TAGID_TERM) || (tagid == 0)) {
+ lvid_sector = sector;
+ if (length > lb_size) {
+ /* space for a terminator */
+ term_sector = sector + num_sectors;
+ }
+ break; /* while */
+ }
+ length -= lb_size;
+ sector += num_sectors;
+
+ if (udf_rw32(dscr->lvid.next_extent.len)) {
+ sector = udf_rw32(dscr->lvid.next_extent.loc);
+ length = udf_rw32(dscr->lvid.next_extent.len);
+ }
+ /* free consumed descriptor */
+ free(dscr);
+ dscr = NULL;
+ }
+ if (dscr) free(dscr);
+
+ if ((!lvid_sector) || (length == 0)) {
+ sector = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.loc);
+ length = udf_rw32(udf_log_vol->log_vol->integrity_seq_loc.len);
+ lvid_sector = sector;
+ if (length > lb_size) {
+ /* space for a terminator */
+ term_sector = lvid_sector + num_sectors;
+ }
+ }
+ assert(lvid_sector);
+
+ /* build up integrity descriptor and write it out */
+ dscr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver);
+ udf_init_desc_tag(&intdesc->tag, TAGID_LOGVOL_INTEGRITY, dscr_ver, udf_log_vol->integrity_serial);
+
+ udf_set_timestamp_now(&intdesc->time);
+ intdesc->integrity_type = udf_rw32(type);
+
+ intdesc->lvint_next_unique_id = udf_rw64(udf_log_vol->next_unique_id);
+
+ /* calculate and fill in free space */
+ intdesc->num_part = udf_rw32(udf_log_vol->num_part_mappings);
+ free_space_pos = &intdesc->tables[0];
+ size_pos = &intdesc->tables[udf_log_vol->num_part_mappings];
+ SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ part_num = part_mapping->udf_virt_part_num;
+ udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition);
+ assert(udf_partition);
+
+ *size_pos++ = udf_partition->partition->part_len;
+ *free_space_pos++ = udf_rw32(udf_partition->free_unalloc_space / udf_log_vol->lb_size);
+ }
+
+ /* fill in UDF implementation use parameters */
+ impl = (struct udf_logvol_info *) (&intdesc->tables[2*udf_log_vol->num_part_mappings]);
+ udf_set_imp_id(&impl->impl_id);
+ impl->num_files = udf_rw32(udf_log_vol->num_files);
+ impl->num_directories = udf_rw32(udf_log_vol->num_directories);
+ impl->min_udf_readver = udf_rw16(udf_log_vol->min_udf_readver);
+ impl->min_udf_writever = udf_rw16(udf_log_vol->min_udf_writever);
+ impl->max_udf_writever = udf_rw16(udf_log_vol->max_udf_writever);
+
+ intdesc->l_iu = udf_rw32(sizeof(struct udf_logvol_info)); /* ECMA 3/10.10.7, UDF 2.2.6.4. */
+ len = sizeof(struct logvol_int_desc) - sizeof(uint32_t); /* length of logvol_int_desc without the extra table entry */
+ len += sizeof(uint32_t) * 2 * udf_log_vol->num_part_mappings; /* size and free space */
+ len += sizeof(struct udf_logvol_info); /* extra implementation use area */
+ len -= UDF_DESC_TAG_LENGTH; /* without header */
+ intdesc->tag.desc_crc_len = udf_rw16(len);
+
+ udf_write_session_descriptor(session, lvid_sector, "Logvol integrity descriptor (LVID)", (union dscrptr *) intdesc, &wr_callback);
+ if (session->disc->rewritable && term_sector) {
+ /* only when there is space and its a rewritable media add a terminor */
+ error = udf_create_empty_terminator_descriptor(lb_size, dscr_ver, &terminator);
+ if (!error) {
+ udf_write_session_descriptor(session, term_sector, "Logvol integrity sequence descriptor sequence terminator", (union dscrptr *) terminator, &wr_callback);
+ free(terminator);
+ }
+ }
+
+ free(intdesc);
+
+ return 0;
+}
+
+
+/* mark the logical volume `open'; for non-rewritables (CD-R/DVD+R/DVD-R) this is allmost a no-op */
+int udf_open_logvol(struct udf_log_vol *udf_log_vol) {
+ int error;
+
+ if (!udf_log_vol->writable) {
+ udf_dump_volume_name("\nLogical volume marked read only: ", udf_log_vol);
+ return EROFS;
+ }
+
+ /* will return many times for each write */
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_OPEN)
+ return 0;
+
+ /*
+ * Opening and closing logical volumes is derived from the state of
+ * the primaries disc.
+ */
+ udf_dump_volume_name("Opening logical volume", udf_log_vol);
+ if (!udf_log_vol->primary->udf_session->disc->sequential) {
+ error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_OPEN);
+ assert(!error);
+ /* sync caches to make sure all is written out */
+ udf_sync_caches(udf_log_vol);
+ /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */
+ } else {
+ /* sequential recordable; any write just opens it; the descriptor is allready marked open */
+ }
+
+ /* mark it open */
+ udf_log_vol->logvol_state = UDF_INTEGRITY_OPEN;
+
+ return 0;
+}
+
+
+/* mark the logical volume in a `closed' state; close the integrity when possible for recordables writeout VAT */
+int udf_close_logvol(struct udf_log_vol *udf_log_vol) {
+ int error;
+
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) {
+ DEBUG(printf("close logvol: integrity allready closed\n"));
+ return 0;
+ }
+
+ /*
+ * Opening and closing logical volumes is derived from the state of
+ * the primaries disc.
+ */
+ udf_dump_volume_name("Closing logical volume", udf_log_vol);
+ if (!udf_log_vol->primary->udf_session->disc->sequential) {
+ error = udf_writeout_LVID(udf_log_vol, UDF_INTEGRITY_CLOSED);
+ assert(!error);
+ } else {
+ /* XXX TODO XXX */
+ fprintf(stderr, "write out virtual sectors, compile VAT and write out VAT : not implemented\n");
+ return EIO;
+ }
+
+ /* sync caches to make sure all is written out */
+ udf_sync_caches(udf_log_vol);
+ /* FIXME (callback) XXX ought to wait until we get the ALL-OK signal from the writeout-LVID action XXX */
+
+ /* mark it closed again */
+ udf_log_vol->logvol_state = UDF_INTEGRITY_CLOSED;
+ return 0;
+}
+
+
+int udf_sync_logvol(struct udf_log_vol *udf_log_vol) {
+ struct udf_node *udf_node;
+ uint32_t num_dirty, count, prnt;
+ int error;
+
+ if (!udf_log_vol->writable)
+ return 0;
+
+ if (udf_log_vol->logvol_state == UDF_INTEGRITY_CLOSED) {
+ DEBUG(printf("close logvol: its closed so no sync nessisary\n"));
+ return 0;
+ }
+
+ UDF_VERBOSE(udf_dump_volume_name("\tsyncing ", udf_log_vol));
+
+ /* sync all nodes */
+ /* XXX syncing logvol sequential due to insertion sort in add node XXX */
+ num_dirty = 0;
+ TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) {
+ num_dirty++;
+ }
+
+ /*
+ * Purge all data out first, this will speed things up later (not
+ * strickly nessissary since syncing a node will wait for all the data
+ * to be written out first anyway
+ */
+ count = num_dirty;
+ prnt = 0;
+ UDF_VERBOSE(printf("\t\tsyncing data\n"));
+ TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) {
+ UDF_VERBOSE(printf("\r%8d", count); fflush(stdout));
+ udf_sync_udf_node(udf_node, "Sync Logvol");
+ count--;
+ prnt = 1;
+ }
+ if (prnt) UDF_VERBOSE(printf("\r \r"));
+
+ /*
+ * Purge all nodes out... they ought to have no dirty buffers anymore
+ * but they will write them out if deemed nessisary
+ */
+ count = num_dirty;
+ prnt = 0;
+ UDF_VERBOSE(printf("\t\tsyncing nodes\n"));
+ TAILQ_FOREACH(udf_node, &udf_log_vol->dirty_nodes, next_dirty) {
+ UDF_VERBOSE(printf("\r%8d", count); fflush(stdout));
+ DEBUG(printf("N"); fflush(stdout));
+ udf_writeout_udf_node(udf_node, "Sync Logvol");
+ count--;
+ prnt = 1;
+ }
+ if (prnt) UDF_VERBOSE(printf("\r \r"));
+
+ /* shouldn't be nessisary */
+ udf_bufcache->flushall = 1;
+ udf_purgethread_kick("Sync Logvol");
+ usleep(1);
+
+ if (udf_bufcache->lru_len_dirty_metadata + udf_bufcache->lru_len_dirty_data) {
+ printf("Warning: after syncing logvol dirty counts != 0 (%d, %d); please contact author.\n",
+ udf_bufcache->lru_len_dirty_metadata, udf_bufcache->lru_len_dirty_data);
+ }
+
+ /* sync free and used space tables for writable volsets */
+ UDF_VERBOSE(printf("\t\tused/freed space tables\n"));
+ error = udf_sync_space_tables(udf_log_vol);
+
+ /* close logical volume */
+ udf_close_logvol(udf_log_vol);
+
+ return error;
+}
+
+
+/* convenience routine */
+int udf_sync_disc(struct udf_discinfo *disc) {
+ struct udf_volumeset *udf_volumeset;
+ struct udf_pri_vol *udf_pri_vol;
+ struct udf_log_vol *udf_log_vol;
+
+ SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) {
+ if (!udf_volumeset->obsolete) {
+ STAILQ_FOREACH(udf_pri_vol, &udf_volumeset->primaries, next_primary) {
+ if (udf_pri_vol->udf_session->disc == disc) {
+ SLIST_FOREACH(udf_log_vol, &udf_pri_vol->log_vols, next_logvol) {
+ udf_sync_logvol(udf_log_vol);
+ } /* logical */
+ } /* disc */
+ } /* primary */
+ } /* if */
+ } /* volumeset */
+
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * UDF descriptor buildup and update functions
+ *
+ ******************************************************************************************/
+
+static void udf_init_desc_tag(struct desc_tag *tag, uint16_t id, uint16_t dscr_ver, uint16_t serial_num) {
+ bzero(tag, sizeof(struct desc_tag));
+ tag->id = udf_rw16(id);
+ tag->descriptor_ver = udf_rw16(dscr_ver);
+ tag->serial_num = udf_rw16(serial_num);
+ /* the rest gets filled in when we write */
+}
+
+
+static void udf_osta_charset(struct charspec *charspec) {
+ bzero(charspec, sizeof(struct charspec));
+ charspec->type = 0;
+ strcpy((char *) charspec->inf, "OSTA Compressed Unicode");
+}
+
+
+static void udf_encode_osta_id(char *osta_id, uint16_t len, char *text) {
+ uint16_t u16_name[1024];
+ uint8_t *pos;
+ uint16_t *pos16;
+
+ bzero(osta_id, len);
+ if (!text) return;
+
+ bzero(u16_name, sizeof(uint16_t) * 1023);
+ /* convert ascii to 16 bits unicode */
+ pos = (uint8_t *) text;
+ pos16 = u16_name;
+ while (*pos) {
+ *pos16 = *pos;
+ pos++; pos16++;
+ }
+ *pos16 = 0;
+
+ udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id);
+
+ /* Ecma 167/7.2.13 states that the length is recorded in the last byte */
+ osta_id[len-1] = strlen(text)+1;
+}
+
+
+static void udf_set_app_id(struct regid *regid) {
+ bzero(regid, sizeof(struct regid));
+ regid->flags = 0; /* not dirty and not protected */
+ strcpy((char *) regid->id, APP_NAME);
+ regid->id_suffix[0] = APP_VERSION_MAIN;
+ regid->id_suffix[1] = APP_VERSION_SUB;
+}
+
+
+static void udf_set_imp_id(struct regid *regid) {
+ bzero(regid, sizeof(struct regid));
+ regid->flags = 0; /* not dirty and not protected */
+ strcpy((char *) regid->id, IMPL_NAME);
+ regid->id_suffix[0] = 4; /* unix */
+ regid->id_suffix[1] = 0; /* generic */
+#if defined(__ANONYMOUSUDF__)
+#elif defined(__NetBSD__)
+ regid->id_suffix[1] = 8; /* NetBSD */
+#elif defined(__FreeBSD__)
+ regid->id_suffix[1] = 7; /* FreeBSD */
+#elif defined(LINUX)
+ regid->id_suffix[1] = 5; /* Linux */
+#endif
+}
+
+
+static void udf_set_entity_id(struct regid *regid, char *name, uint16_t UDF_version) {
+ uint16_t *ver;
+
+ bzero(regid, sizeof(struct regid));
+ regid->flags = 0; /* not dirty and not protected */
+ strcpy((char *) regid->id, name);
+ ver = (uint16_t *) regid->id_suffix;
+ *ver = udf_rw16(UDF_version);
+ regid->id_suffix[2] = 4; /* unix */
+ regid->id_suffix[3] = 0; /* generic */
+#if defined(__ANONYMOUSUDF__)
+#elif defined(__NetBSD__)
+ regid->id_suffix[3] = 8; /* NetBSD */
+#elif defined(__FreeBSD__)
+ regid->id_suffix[3] = 7; /* FreeBSD */
+#elif defined(LINUX)
+ regid->id_suffix[3] = 5; /* Linux */
+#endif
+}
+
+
+void udf_set_contents_id(struct regid *regid, char *content_id) {
+ bzero(regid, sizeof(struct regid));
+ regid->flags = 0;
+ strcpy((char *) regid->id, content_id);
+}
+
+
+/* XXX creators of empty descriptors could be externalised */
+
+/*
+ * result can be further processed using modify functions if demanded and then
+ * processed trough udf_proc_pri_vol
+ * [ int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming); ]
+ *
+ */
+
+int udf_create_empty_primary_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *volset_id, char *privol_name, int vds_num, int max_vol_seq, struct pri_vol_desc **dscrptr) {
+ struct pri_vol_desc *dscr;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate an empty primary volume descriptor */
+ dscr = malloc(sector_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_PRI_VOL, dscr_ver, 1);
+ dscr->pvd_num = udf_rw32(serial);
+ udf_encode_osta_id(dscr->vol_id, 32, privol_name);
+ dscr->vds_num = udf_rw16(vds_num);
+ dscr->max_vol_seq = udf_rw16(max_vol_seq);
+ if (max_vol_seq > 1) {
+ dscr->ichg_lvl = udf_rw16(3); /* signal its a single volume intended to be in a set */
+ dscr->max_ichg_lvl = udf_rw16(3); /* ,, */
+ dscr->flags = udf_rw16(1); /* signal relevance volumeset id */
+ } else {
+ dscr->ichg_lvl = udf_rw16(2); /* signal its volume intended not to be in a set */
+ dscr->max_ichg_lvl = udf_rw16(2); /* ,, */
+ dscr->flags = udf_rw16(0); /* signal relevance volumeset id */
+ }
+
+ dscr->charset_list = udf_rw32(1); /* only CS0 */
+ dscr->max_charset_list = udf_rw32(1);
+ udf_encode_osta_id(dscr->volset_id, 128, volset_id);
+ udf_osta_charset(&dscr->desc_charset);
+ udf_osta_charset(&dscr->explanatory_charset);
+ udf_set_app_id(&dscr->app_id);
+ udf_set_imp_id(&dscr->imp_id);
+ udf_set_timestamp_now(&dscr->time);
+
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+ return 0;
+}
+
+
+int udf_create_empty_partition_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, uint16_t part_num, uint32_t access_type, uint32_t start_loc, uint32_t part_len, uint32_t space_bitmap_size, uint32_t unalloc_space_bitmap, struct part_desc **dscrptr) {
+ struct part_desc *dscr;
+ struct part_hdr_desc *part_hdr;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate empty partition descriptor */
+ dscr = malloc(sector_size); /* only descriptor, no bitmap! */
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_PARTITION, dscr_ver, 1);
+ dscr->seq_num = udf_rw32(serial);
+ dscr->flags = udf_rw16(1); /* bit 0 : space is allocated */
+ dscr->part_num = udf_rw16(part_num);
+
+ if (dscr_ver == 2) udf_set_contents_id(&dscr->contents, "+NSR02");
+ if (dscr_ver == 3) udf_set_contents_id(&dscr->contents, "+NSR03");
+ part_hdr = &dscr->pd_part_hdr;
+ part_hdr->unalloc_space_bitmap.len = udf_rw32(space_bitmap_size);
+ part_hdr->unalloc_space_bitmap.lb_num = udf_rw32(unalloc_space_bitmap);
+
+ dscr->access_type = udf_rw32(access_type);
+ dscr->start_loc = udf_rw32(start_loc);
+ dscr->part_len = udf_rw32(part_len);
+
+ udf_set_imp_id(&dscr->imp_id); /* why is this ignored? */
+
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+ return 0;
+}
+
+
+int udf_create_empty_unallocated_space_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, struct unalloc_sp_desc **dscrptr) {
+ struct unalloc_sp_desc *dscr;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate an empty unallocated space descriptor */
+ dscr = malloc(sector_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_UNALLOC_SPACE, dscr_ver, 1);
+ dscr->seq_num = udf_rw32(serial);
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad) - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+
+ return 0;
+}
+
+
+int udf_create_empty_implementation_use_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, struct impvol_desc **dscrptr) {
+ struct impvol_desc *dscr;
+ struct udf_lv_info *lv_info;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate an empty implementation use volume descriptor */
+ dscr = malloc(sector_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_IMP_VOL, dscr_ver, 1);
+ dscr->seq_num = udf_rw32(serial);
+ udf_set_entity_id(&dscr->impl_id, "*UDF LV Info", 0x102); /* just pick one; it'll be modifed later */
+
+ lv_info = &dscr->_impl_use.lv_info;
+ udf_osta_charset(&lv_info->lvi_charset);
+ udf_encode_osta_id(lv_info->logvol_id, 128, logvol_name);
+ udf_encode_osta_id(lv_info->lvinfo1, 36, NULL);
+ udf_encode_osta_id(lv_info->lvinfo2, 36, NULL);
+ udf_encode_osta_id(lv_info->lvinfo3, 36, NULL);
+ udf_set_imp_id(&lv_info->impl_id);
+
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+
+ return 0;
+}
+
+
+int udf_create_empty_logical_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, uint32_t lb_size, uint32_t integrity_start, uint32_t integrity_length, struct logvol_desc **dscrptr) {
+ struct logvol_desc *dscr;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate an empty logical volume descriptor */
+ dscr = malloc(sector_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_LOGVOL, dscr_ver, 1);
+ dscr->seq_num = udf_rw32(serial);
+ udf_osta_charset(&dscr->desc_charset);
+ udf_encode_osta_id(dscr->logvol_id, 128, logvol_name);
+ dscr->lb_size = udf_rw32(lb_size);
+ udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant");
+
+ /* no fsd yet nor partition mapping */
+ udf_set_imp_id(&dscr->imp_id);
+ dscr->integrity_seq_loc.loc = udf_rw32(integrity_start);
+ dscr->integrity_seq_loc.len = udf_rw32(integrity_length * lb_size);
+
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+ return 0;
+}
+
+
+int udf_create_empty_space_bitmap(uint32_t sector_size, uint16_t dscr_ver, uint32_t num_lbs, struct space_bitmap_desc **dscrptr) {
+ struct space_bitmap_desc *dscr;
+ uint64_t bits;
+ uint32_t bytes, space_bitmap_size;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* reserve space for unallocated space bitmap */
+ bits = num_lbs;
+ bytes = (bits + 7)/8;
+ space_bitmap_size = (bytes + sizeof(struct space_bitmap_desc)-1);
+
+ /* round space bitmap size to sector size */
+ space_bitmap_size = ((space_bitmap_size + sector_size - 1) / sector_size) * sector_size;
+
+ /* allocate and populate an empty space bitmap descriptor */
+ dscr = malloc(space_bitmap_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, space_bitmap_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_SPACE_BITMAP, dscr_ver, 1);
+ /* crc length 8 is recommended, UDF 2.3.1.2, 2.3.8.1, errata DCN-5108 for UDF 2.50 and lower. */
+ dscr->tag.desc_crc_len = udf_rw16(8);
+
+ dscr->num_bits = udf_rw32(bits);
+ dscr->num_bytes = udf_rw32(bytes);
+
+ *dscrptr = dscr;
+ return 0;
+}
+
+
+/* FIXME: no rootdir setting yet */
+/* FIXME: fileset desc. is disc sector size or lb_size ? */
+int udf_create_empty_fileset_desc(uint32_t sector_size, uint16_t dscr_ver, uint32_t fileset_num, char *logvol_name, char *fileset_name, struct fileset_desc **dscrptr) {
+ struct fileset_desc *dscr;
+
+ assert(dscrptr);
+ *dscrptr = NULL;
+
+ /* allocate and populate an empty logical volume descriptor */
+ dscr = malloc(sector_size);
+ if (!dscr) return ENOMEM;
+ bzero(dscr, sector_size);
+
+ udf_init_desc_tag(&dscr->tag, TAGID_FSD, dscr_ver, 1);
+ udf_set_timestamp_now(&dscr->time);
+ dscr->ichg_lvl = udf_rw16(3); /* fixed? */
+ dscr->max_ichg_lvl = udf_rw16(3); /* fixed? */
+ dscr->charset_list = udf_rw32(1); /* only CS0 */
+ dscr->max_charset_list = udf_rw32(1); /* only CS0 */
+ dscr->fileset_num = udf_rw32(fileset_num); /* key for fileset */
+ dscr->fileset_desc_num = udf_rw32(0); /* fileset descriptor number as in copy # */
+
+ udf_osta_charset(&dscr->logvol_id_charset);
+ udf_encode_osta_id(dscr->logvol_id, 128, logvol_name);
+
+ udf_osta_charset(&dscr->fileset_charset);
+ udf_encode_osta_id(dscr->fileset_id, 32, fileset_name);
+
+ udf_encode_osta_id(dscr->copyright_file_id, 32, NULL);
+ udf_encode_osta_id(dscr->abstract_file_id, 32, NULL);
+
+ udf_set_contents_id(&dscr->domain_id, "*OSTA UDF Compliant");
+
+ dscr->tag.desc_crc_len = udf_rw16(sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH);
+
+ *dscrptr = dscr;
+ return 0;
+}
+
+
+int udf_create_empty_anchor_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint32_t main_vds_loc, uint32_t reserve_vds_loc, uint32_t length, struct anchor_vdp **vdp) {
+ assert(vdp);
+ assert(abs(main_vds_loc - reserve_vds_loc) >= length);
+
+ *vdp = malloc(sector_size);
+ if (!*vdp) return ENOMEM;
+ bzero(*vdp, sector_size);
+
+ udf_init_desc_tag(&(*vdp)->tag, TAGID_ANCHOR, dscr_ver, 1);
+ (*vdp)->main_vds_ex.loc = udf_rw32(main_vds_loc);
+ (*vdp)->main_vds_ex.len = udf_rw32(length * sector_size);
+ (*vdp)->reserve_vds_ex.loc = udf_rw32(reserve_vds_loc);
+ (*vdp)->reserve_vds_ex.len = udf_rw32(length * sector_size);
+
+ (*vdp)->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */
+ return 0;
+}
+
+
+int udf_create_empty_terminator_descriptor(uint32_t sector_size, uint16_t dscr_ver, struct desc_tag **tag) {
+ assert(tag);
+
+ *tag = malloc(sector_size);
+ if (!*tag) return ENOMEM;
+ bzero(*tag, sector_size);
+
+ udf_init_desc_tag(*tag, TAGID_TERM, dscr_ver, 1);
+
+ (*tag)->desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); /* fixed size in Ecma */
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * Basic `open' and `close' disc functions
+ *
+ ******************************************************************************************/
+
+
+static void udf_process_session_range(struct udf_discinfo *disc, int *enabled, int low, int high) {
+ int session;
+
+ if (!disc) return;
+
+ high = MIN(high, disc->num_sessions-1);
+ session = low;
+
+ for (session = low; session <= high; session++) {
+ enabled[session] = 1;
+ }
+}
+
+
+/* range is specified in -3,5,7 or 5-6,8- etc */
+static int udf_process_session_range_string(struct udf_discinfo *disc, char *range) {
+ struct udf_session *udf_session, *next_udf_session, *last_ok;
+ char *pos, *nop;
+ int low, high, len, session;
+ int enabled[MAX_SESSIONS];
+
+ if (!range) return 0;
+ DEBUG(printf("UDF range debugging string '%s'\n", range));
+
+ if (disc) {
+ /* disable all */
+ for (session = 0; session < disc->num_sessions; session++) {
+ enabled[session] = 0;
+ }
+ }
+
+ /* parse string */
+ nop = strdup(range);
+ pos = range;
+ if (sscanf(pos, "-%u%n%s", &high, &len, nop) >= 1) {
+ DEBUG(printf("UDF range match till %d\n", high));
+ udf_process_session_range(disc, enabled, 0, high);
+ pos += len;
+ }
+ if (*pos && *pos == ',') pos++;
+ while (*pos) {
+ if (sscanf(pos, "%u%n%s", &low, &len, nop) >= 1) {
+ pos += len;
+ if (*pos == '-') {
+ pos++;
+ if (!*pos) {
+ DEBUG(printf("UDF range match from %d\n", low));
+ udf_process_session_range(disc, enabled, low, INT_MAX);
+ free(nop);
+ return 0;
+ }
+ if (sscanf(pos, "%u%n%s", &high, &len, nop) >= 1) {
+ pos += len;
+ DEBUG(printf("UDF range match from %d to %d\n", low, high));
+ udf_process_session_range(disc, enabled, low, high);
+ }
+ } else {
+ if (!*pos || (*pos == ',')) {
+ DEBUG(printf("UDF range match %d\n", low));
+ udf_process_session_range(disc, enabled, low, low);
+ }
+ }
+ if (*pos && (*pos != ',')) {
+ fprintf(stderr, "UDF range matching : ',' expected at %s\n", pos);
+ free(nop);
+ return ENOENT;
+ }
+ pos++;
+ } else {
+ fprintf(stderr, "UDF range matching : number expected at %s\n", pos);
+ free(nop);
+ return ENOENT;
+ }
+ }
+ free(nop);
+
+ DEBUG(printf("UDF range matching : all ok till the end\n"));
+ if (!disc) return 0;
+
+ last_ok = NULL;
+ udf_session = STAILQ_FIRST(&disc->sessions);
+ while (udf_session) {
+ next_udf_session = STAILQ_NEXT(udf_session, next_session);
+ session = udf_session->session_num;
+ if (!enabled[session]) {
+ /* remove this session */
+ fprintf(stderr, "UDF: disabling UDF session %d on request\n", session);
+ STAILQ_REMOVE(&disc->sessions, udf_session, udf_session, next_session);
+ free(udf_session);
+
+ disc->session_is_UDF[session] = 0;
+ }
+ udf_session = next_udf_session;
+ }
+
+ return 0;
+}
+
+
+int udf_check_session_range(char *range) {
+ return udf_process_session_range_string(NULL, range);
+}
+
+
+void
+udf_init(void)
+{
+ udf_unix_init();
+ udf_start_unix_thread();
+ dirhash_init();
+
+ SLIST_INIT(&udf_discs_list);
+}
+
+
+int udf_mount_disc(char *devname, char *range, uint32_t sector_size, int mnt_flags, struct udf_discinfo **disc) {
+ int discop_flags, error;
+
+ discop_flags = mnt_flags & UDF_MNT_BSWAP ? UDF_DISCOP_BSWAP : 0;
+ error = udf_open_disc(devname, discop_flags, disc);
+ if ((!error) && sector_size)
+ error = udf_discinfo_alter_perception(*disc, sector_size, 0);
+ if (error)
+ return error;
+
+ error = udf_get_anchors(*disc);
+ UDF_VERBOSE(udf_dump_disc_anchors(*disc));
+
+ if (range) {
+ UDF_VERBOSE(printf("Selecting UDF sessions '%s' as specified\n", range));
+ udf_process_session_range_string(*disc, range);
+ UDF_VERBOSE(udf_dump_disc_anchors(*disc));
+ }
+
+ /* no UDF partitions so bail out */
+ if ((*disc)->num_udf_sessions == 0) return 0;
+
+ UDF_VERBOSE(printf("Start mounting\n"));
+ error = udf_get_volumeset_space(*disc);
+ if (error) return error;
+
+ UDF_VERBOSE(printf("\teliminating predescessors\n"));
+ udf_eliminate_predescessor_volumesets(*disc);
+
+ UDF_VERBOSE_TABLES(udf_dump_alive_sets());
+
+ UDF_VERBOSE(printf("\tretrieving logical volume dependencies %p\n", *disc));
+ error = udf_get_logical_volumes_supporting_tables(*disc, mnt_flags);
+
+ UDF_VERBOSE_TABLES(udf_dump_alive_sets());
+
+ /* insert disc in the disc list */
+ SLIST_INSERT_HEAD(&udf_discs_list, *disc, next_disc);
+
+ return error;
+}
+
+
+int udf_dismount_disc(struct udf_discinfo *disc) {
+ UDF_VERBOSE(printf("Dismounting disc\n"));
+ if (!disc->recordable) {
+ /* easy way out: it was a read-only system */
+ UDF_VERBOSE(printf("\tdismounting readonly disc\n"));
+ udf_stop_unix_thread();
+ udf_close_disc(disc);
+ return 0;
+ }
+
+ /* Sync disc before closing it */
+ UDF_VERBOSE(printf("\tsyncing disc\n"));
+ udf_sync_disc(disc);
+
+ /* wait for the disc to idle */
+ UDF_VERBOSE(printf("\twait for syncing disc to idle\n"));
+ while (!udf_discinfo_check_disc_ready(disc)) {
+ sleep(1);
+ }
+
+ /* stop threads and finish writing to it */
+ udf_stop_unix_thread();
+
+ UDF_VERBOSE(printf("\tsignal disc its finished with writing\n"));
+ udf_discinfo_finish_writing(disc);
+
+ /* wait for the disc to idle again */
+ UDF_VERBOSE(printf("\twait for final disc idling\n"));
+ while (!udf_discinfo_check_disc_ready(disc)) {
+ sleep(1);
+ }
+
+ UDF_VERBOSE(printf("\tclose device\n"));
+ udf_close_disc(disc);
+
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * Directory and other conversion UDF logic
+ * Move to udf_unix.c / udf_vnops.c one day?
+ *
+ ******************************************************************************************/
+
+static int udf_translate_icb_filetype_to_dirent_filetype(int udf_filetype) {
+ int d_type;
+
+ switch (udf_filetype) {
+ case UDF_ICB_FILETYPE_DIRECTORY :
+ d_type = DT_DIR;
+ break;
+ case UDF_ICB_FILETYPE_STREAMDIR :
+ d_type = DT_DIR;
+ break;
+ case UDF_ICB_FILETYPE_FIFO :
+ d_type = DT_FIFO;
+ break;
+ case UDF_ICB_FILETYPE_CHARDEVICE :
+ d_type = DT_CHR;
+ break;
+ case UDF_ICB_FILETYPE_BLOCKDEVICE :
+ d_type = DT_BLK;
+ break;
+ case UDF_ICB_FILETYPE_RANDOMACCESS :
+ d_type = DT_REG;
+ break;
+ case UDF_ICB_FILETYPE_SYMLINK :
+ d_type = DT_LNK;
+ break;
+ case UDF_ICB_FILETYPE_SOCKET :
+ d_type = DT_SOCK;
+ break;
+ default :
+ d_type = DT_UNKNOWN;
+ break;
+ }
+ return d_type;
+}
+
+
+/* VOP_GETATTR */
+/* allmost NOOP since we remember the stat in the inode */
+int udf_getattr(struct udf_node *udf_node, struct stat *stat) {
+ *stat = udf_node->stat;
+
+ /* special: updatables */
+ stat->st_nlink = udf_node->link_cnt;
+ stat->st_blocks = (stat->st_size + 512 -1)/512; /* blocks are hardcoded 512 bytes/sector in stat :-/ */
+ return 0;
+}
+
+
+/* VOP_SETATTR */
+/* allmost NOOP since we remember the stat in the inode */
+/* note VOP_SETATTR can selectively set attrs */
+int udf_setattr(struct udf_node *udf_node, struct stat *stat) {
+ if (!udf_node) return ENOENT;
+
+ if (udf_open_logvol(udf_node->udf_log_vol))
+ return EROFS;
+
+ /* FIXME please don't just copy everything ... XXX */
+ udf_node->stat = *stat;
+
+ /* not attribute change time */
+ udf_set_timespec_now(&udf_node->stat.st_ctimespec);
+
+ udf_node_mark_dirty(udf_node);
+ return 0;
+}
+
+
+void udf_resync_fid_stream(uint8_t *buffer, uint32_t *pfid_pos, uint32_t max_fid_pos, int *phas_fids) {
+ struct fileid_desc *fid;
+ uint32_t fid_pos;
+ int has_fids;
+
+ assert(buffer);
+ assert(pfid_pos);
+ assert(phas_fids);
+
+ has_fids = 0;
+ fid_pos = *pfid_pos;
+ while (!has_fids) {
+ while (fid_pos <= max_fid_pos) {
+ fid = (struct fileid_desc *) (buffer + fid_pos);
+ if (udf_rw16(fid->tag.id) == TAGID_FID)
+ break;
+ /* fid's can only exist 4 bytes aligned */
+ fid_pos += 4;
+ }
+ if (fid_pos > max_fid_pos) {
+ /* shouldn't happen ! to prevent chaos, do nothing */
+ /* XXX ought to give a warning? XXX */
+ has_fids = 0;
+ break;
+ } else {
+ /* check if we found a valid FID */
+ fid = (struct fileid_desc *) (buffer + fid_pos);
+ has_fids = (udf_check_tag((union dscrptr *) fid) == 0);
+ if (has_fids) {
+ assert(udf_rw16(fid->tag.id) == TAGID_FID);
+ break;
+ }
+ }
+ }
+ *pfid_pos = fid_pos;
+ *phas_fids = has_fids;
+}
+
+
+/* read one fid and process it into a dirent and advance to the next */
+/* (*fid) has to be allocated a logical block in size, (*dirent) struct dirent length */
+int udf_read_fid_stream(struct udf_node *dir_node, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent) {
+ struct uio dir_uio;
+ struct iovec dir_iovec;
+ char *fid_name;
+ uint32_t entry_length, lb_size;
+ int enough, error;
+
+ assert(fid);
+ assert(dirent);
+ assert(dir_node);
+ assert(offset);
+ assert(*offset != 1);
+
+ lb_size = dir_node->udf_log_vol->lb_size;
+ entry_length = 0;
+ bzero(dirent, sizeof(struct dirent));
+ bzero(fid, lb_size);
+
+ if (*offset >= (uint64_t) dir_node->stat.st_size)
+ return EINVAL;
+
+ bzero(&dir_uio, sizeof(struct uio));
+ dir_uio.uio_rw = UIO_WRITE; /* write into this space */
+ dir_uio.uio_iovcnt = 1;
+ dir_uio.uio_iov = &dir_iovec;
+ dir_iovec.iov_base = fid;
+ dir_iovec.iov_len = lb_size;
+ dir_uio.uio_offset = *offset;
+ dir_uio.uio_resid = MIN(dir_node->stat.st_size - (*offset), lb_size);
+
+ error = udf_read_file_part_uio(dir_node, "file id" /* udf_node->dirent.d_name */, UDF_C_FIDS, &dir_uio);
+ if (error)
+ return error;
+
+ /*
+ * Check if we got a whole descriptor.
+ * XXX Try to `resync' directory stream when something is very wrong.
+ *
+ */
+ enough = (dir_uio.uio_offset - (*offset) >= UDF_FID_SIZE);
+ if (!enough) {
+ /* short dir ... */
+ return EIO;
+ }
+
+ error = udf_check_tag((union dscrptr *) fid);
+ if (!error) {
+ entry_length = udf_calc_tag_malloc_size((union dscrptr *) fid, lb_size);
+ enough = (dir_uio.uio_offset - (*offset) >= entry_length);
+ }
+ if (!enough) {
+ /* short dir ... */
+ return EIO;
+ }
+
+ if (!error) error = udf_check_tag_payload((union dscrptr *) fid);
+ if (error) {
+ printf("BROKEN DIRECTORY ENTRY\n");
+#if 0
+ // udf_dump_desc(&fid->tag);
+ // udf_dump_fileid(fid);
+#endif
+ /* RESYNC? */
+ /* TODO: use udf_resync_fid_stream */
+ return EIO;
+ }
+
+ /* we got a whole and valid descriptor */
+ /* create resulting dirent structure */
+ fid_name = (char *) fid->data + udf_rw16(fid->l_iu);
+ dirent->d_fileno = udf_rw32(fid->icb.impl.im_used.unique_id); /* only 32 bits salvageable */
+#if !defined(__DragonFly__)
+ dirent->d_reclen = sizeof(struct dirent);
+#endif
+ dirent->d_type = DT_UNKNOWN;
+ udf_to_unix_name(dirent->d_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset);
+#ifndef NO_DIRENT_NAMLEN
+ dirent->d_namlen = strlen(dirent->d_name);
+#endif
+
+ if (fid->file_char & UDF_FILE_CHAR_DIR) dirent->d_type = DT_DIR;
+ if (fid->file_char & UDF_FILE_CHAR_PAR) strcpy(dirent->d_name, "..");
+
+ /* advance */
+ *offset += entry_length;
+
+ return error;
+}
+
+
+/* VOP_READDIR */
+/* read in dirent's until the result_uio can't hold another */
+int udf_readdir(struct udf_node *dir_node, struct uio *result_uio, int *eof_res /* int *cookies, int ncookies */) {
+ struct fileid_desc *fid;
+ struct dirent dirent;
+ uint64_t diroffset, transoffset;
+ uint32_t lb_size;
+ int eof, enough;
+ int error;
+
+ assert(eof_res);
+ if (!dir_node)
+ return EINVAL;
+ if (!dir_node->udf_log_vol)
+ return EINVAL;
+
+ assert(result_uio->uio_resid >= sizeof(struct dirent));
+ lb_size = dir_node->udf_log_vol->lb_size;
+
+ fid = malloc(lb_size);
+ if (!fid) return ENOMEM;
+
+ /* check if we ought to insert dummy `.' node */
+ if (result_uio->uio_offset == 0) {
+ bzero(&dirent, sizeof(struct dirent));
+ strcpy(dirent.d_name, ".");
+ dirent.d_type = DT_DIR;
+#ifndef NO_DIRENT_NAMLEN
+ dirent.d_namlen = 2;
+#endif
+ uiomove(&dirent, sizeof(struct dirent), result_uio);
+
+ /* mark with magic value (yeah it suxxs) that we have done the dummy */
+ result_uio->uio_offset = 1;
+ }
+
+ /* start directory reading */
+ diroffset = result_uio->uio_offset;
+ transoffset = diroffset;
+ while (diroffset < (uint64_t) dir_node->stat.st_size) {
+ /* read just the offset when its flagged */
+ if (diroffset == 1) {
+ diroffset = result_uio->uio_offset = 0;
+ }
+
+ /* read in FIDs */
+ error = udf_read_fid_stream(dir_node, &diroffset, fid, &dirent);
+ if (error) {
+ printf("Error while reading directory file: %s\n", strerror(error));
+ free(fid);
+ return error;
+ }
+
+ /* if there is not enough space for the dirent break off read */
+ if (result_uio->uio_resid < sizeof(struct dirent))
+ break;
+
+ /* remember the last entry we transfered */
+ transoffset = diroffset;
+
+ /* skip deleted entries */
+ if (fid->file_char & UDF_FILE_CHAR_DEL)
+ continue;
+
+ /* skip not visible entries */
+ if (fid->file_char & UDF_FILE_CHAR_VIS)
+ continue;
+
+ uiomove(&dirent, sizeof(struct dirent), result_uio);
+ }
+
+ /* pass on last transfered offset */
+ result_uio->uio_offset = transoffset;
+
+ free(fid);
+
+ eof = (result_uio->uio_offset >= (uint64_t) dir_node->stat.st_size);
+ if (eof_res) *eof_res = 1;
+ *eof_res = eof;
+
+ return 0;
+}
+
+
+static int
+dirhash_fill(struct udf_node *dir_node)
+{
+ struct dirhash *dirh;
+ struct fileid_desc *fid;
+ struct dirent *dirent;
+ uint64_t file_size, pre_diroffset, diroffset;
+ uint32_t lb_size;
+ int error;
+
+ /* make sure we have a dirhash to work on */
+ dirh = dir_node->dir_hash;
+ assert(dirh);
+ assert(dirh->refcnt > 0);
+
+ if (dirh->flags & DIRH_BROKEN)
+ return EIO;
+ if (dirh->flags & DIRH_COMPLETE)
+ return 0;
+
+ /* make sure we have a clean dirhash to add to */
+ dirhash_purge_entries(dirh);
+
+ /* get directory filesize */
+ file_size = dir_node->stat.st_size;
+
+ /* allocate temporary space for fid */
+ lb_size = dir_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ assert(fid);
+
+ /* allocate temporary space for dirent */
+ dirent = malloc(sizeof(struct dirent));
+ assert(dirent);
+
+ error = 0;
+ diroffset = 0;
+ while (diroffset < file_size) {
+ /* transfer a new fid/dirent */
+ pre_diroffset = diroffset;
+ error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent);
+ if (error) {
+ /* TODO what to do? continue but not add? */
+ dirh->flags |= DIRH_BROKEN;
+ dirhash_purge_entries(dirh);
+ break;
+ }
+
+ if ((fid->file_char & UDF_FILE_CHAR_DEL)) {
+ /* register deleted extent for reuse */
+ dirhash_enter_freed(dirh, pre_diroffset,
+ udf_fidsize(fid));
+ } else {
+ /* append to the dirhash */
+ dirhash_enter(dirh, dirent, pre_diroffset,
+ udf_fidsize(fid), 0);
+
+ /* XXX speedup HACK: preread in our nodes to compensate for too lazy backend */
+ {
+ struct udf_node *res_node;
+ error = udf_readin_udf_node(dir_node, &fid->icb, fid, &res_node);
+ }
+ }
+ }
+ dirh->flags |= DIRH_COMPLETE;
+
+ free(fid);
+ free(dirent);
+
+ return error;
+}
+
+
+int udf_lookup_name_in_dir(struct udf_node *dir_node, char *name, int namelen, struct long_ad *icb_loc, struct fileid_desc *fid, int *found) {
+ struct dirhash *dirh;
+ struct dirhash_entry *dirh_ep;
+ struct dirent *dirent;
+ uint64_t diroffset;
+ int hit, error;
+
+ /* set default return */
+ *found = 0;
+
+ /* get our dirhash and make sure its read in */
+ dirhash_get(&dir_node->dir_hash);
+ error = dirhash_fill(dir_node);
+ if (error) {
+ dirhash_put(dir_node->dir_hash);
+ return error;
+ }
+ dirh = dir_node->dir_hash;
+
+ /* allocate temporary space for dirent */
+ dirent = malloc(sizeof(struct dirent));
+ if (!dirent)
+ return ENOMEM;
+
+ DEBUG(printf("dirhash_lookup looking for `%*.*s`\n",
+ namelen, namelen, name));
+
+ /* search our dirhash hits */
+ memset(icb_loc, 0, sizeof(*icb_loc));
+ dirh_ep = NULL;
+ for (;;) {
+ hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+ /* if no hit, abort the search */
+ if (!hit)
+ break;
+
+ /* check this hit */
+ diroffset = dirh_ep->offset;
+
+ /* transfer a new fid/dirent */
+ error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent);
+ if (error)
+ break;
+
+ DEBUG(printf("dirhash_lookup\tchecking `%*.*s`\n",
+ (int) DIRENT_NAMLEN(dirent), (int) DIRENT_NAMLEN(dirent), dirent->d_name));
+
+ /* see if its our entry */
+ assert(DIRENT_NAMLEN(dirent) == namelen);
+ if (strncmp(dirent->d_name, name, namelen) == 0) {
+ *found = 1;
+ *icb_loc = fid->icb;
+ break;
+ }
+ }
+ free(dirent);
+
+ dirhash_put(dir_node->dir_hash);
+
+ return error;
+}
+
+
+static int udf_count_direntries(struct udf_node *dir_node, int count_dotdot, uint32_t *dir_entries) {
+ struct fileid_desc *fid;
+ struct dirent dirent;
+ uint64_t pos;
+ uint32_t lb_size;
+ int eof;
+ int error;
+
+ if (!dir_node) return EINVAL;
+ lb_size = dir_node->udf_log_vol->lb_size;
+
+ /* count all directory entries with optional the dotdot too */
+ /* only defined in directories XXX DT_COMP also possible XXX */
+ if ((dir_node->stat.st_mode & S_IFDIR) == 0)
+ return ENOTDIR;
+
+ /* get space to read fid in */
+ fid = malloc(lb_size);
+ if (!fid) return ENOMEM;
+
+ /* start directory reading */
+ *dir_entries = 0;
+ pos = 0;
+
+ eof = (pos == (uint64_t) dir_node->stat.st_size);
+ while (!eof) {
+ /* read in FIDs */
+ error = udf_read_fid_stream(dir_node, &pos, fid, &dirent);
+ if (error) {
+ printf("Error while counting directory entries : %s\n", strerror(error));
+ free(fid);
+ return error;
+ }
+
+ /* process this FID/dirent */
+ if ((fid->file_char & UDF_FILE_CHAR_DEL) == 0) {
+ if (fid->file_char & UDF_FILE_CHAR_PAR) {
+ if (count_dotdot) *dir_entries = *dir_entries + 1;
+ } else {
+ *dir_entries = *dir_entries + 1;
+ }
+ }
+ /* pos is automatically advanced */
+ eof = (pos == (uint64_t) dir_node->stat.st_size);
+ }
+ /* end of directory */
+ free(fid);
+
+ return 0;
+}
+
+
+static int udf_writeout_fid_info(struct udf_node *dir_node, struct fileid_desc *fid, uint64_t offset, uint16_t fid_len) {
+ struct uio uio;
+ struct iovec iovec;
+ int flags;
+
+ bzero(&uio, sizeof(struct uio));
+ uio.uio_rw = UIO_READ; /* read from this space */
+ uio.uio_iovcnt = 1;
+ uio.uio_iov = &iovec;
+ iovec.iov_base = fid;
+ iovec.iov_len = fid_len;
+ uio.uio_offset = offset;
+ uio.uio_resid = fid_len;
+
+ flags = UDF_C_FIDS;
+ return udf_write_file_part_uio(dir_node, "file id.", flags, &uio);
+}
+
+
+/* search for a space to record the fid in, not checking if it is allready in it ! */
+/* ALERT: not to be used to update a fid ... use writeout_fid_info for that */
+/* ONLY used by udf_create_directory_entry */
+static int udf_insert_fid_info(struct udf_node *dir_node, struct udf_node *udf_node, struct fileid_desc *i_fid, uint16_t fidsize) {
+ struct dirhash *dirh;
+ struct dirhash_entry *dirh_ep;
+ struct fileid_desc *fid;
+ struct dirent dirent;
+ uint64_t dir_size, fid_pos, chosen_fid_pos, end_fid_pos;
+ uint32_t this_fidsize, chosen_size;
+ uint32_t lb_size, lb_rest;
+ uint32_t size_diff, chosen_size_diff;
+ char *fid_name;
+ int descr_ver, hit, error;
+
+ udf_node = udf_node; /* passed only for printing diagnostic info if required */
+
+ if (!dir_node)
+ return EINVAL;
+
+ /* only defined in directories XXX DT_COMP also possible XXX */
+ if ((dir_node->stat.st_mode & S_IFDIR) == 0)
+ return ENOTDIR;
+
+ /* needs to be 4 bytes aligned to be legal! if not, something is seriously wrong so abort */
+ assert((fidsize & 3) == 0);
+
+ /* get our dirhash and make sure its read in */
+ dirhash_get(&dir_node->dir_hash);
+ error = dirhash_fill(dir_node);
+ if (error) {
+ dirhash_put(dir_node->dir_hash);
+ return error;
+ }
+ dirh = dir_node->dir_hash;
+
+ /* get info */
+ lb_size = dir_node->udf_log_vol->lb_size;
+ dir_size = dir_node->stat.st_size;
+ descr_ver = udf_rw16(dir_node->udf_log_vol->log_vol->tag.descriptor_ver);
+
+ /* get space to read fid in */
+ fid = malloc(lb_size);
+ if (!fid)
+ return ENOMEM;
+
+ /* find position that will fit the FID */
+ chosen_fid_pos = dir_size;
+ chosen_size = 0;
+ chosen_size_diff = UINT_MAX;
+
+ /* shut up gcc */
+#ifndef NO_DIRENT_NAMLEN
+ dirent.d_namlen = 0;
+#endif
+
+ /* search our dirhash hits */
+ error = 0;
+ dirh_ep = NULL;
+ for (;;) {
+ hit = dirhash_lookup_freed(dirh, fidsize, &dirh_ep);
+ /* if no hit, abort the search */
+ if (!hit)
+ break;
+
+ /* check this hit for size */
+ this_fidsize = dirh_ep->entry_size;
+
+ /* check this hit */
+ fid_pos = dirh_ep->offset;
+ end_fid_pos = fid_pos + this_fidsize;
+ size_diff = this_fidsize - fidsize;
+ lb_rest = lb_size - (end_fid_pos % lb_size);
+
+ /* select if not splitting the tag and its smaller */
+ if ((size_diff <= chosen_size_diff) &&
+ (lb_rest >= sizeof(struct desc_tag)))
+ {
+ /* UDF 2.3.4.2+3 specifies rules for iu size */
+ if ((size_diff == 0) || (size_diff >= 32)) {
+ chosen_fid_pos = fid_pos;
+ chosen_size = this_fidsize;
+ chosen_size_diff = size_diff;
+ }
+ }
+ }
+
+ /* extend directory if no other candidate found */
+ if (chosen_size == 0) {
+ chosen_fid_pos = dir_size;
+ chosen_size = fidsize;
+
+ /* special case UDF 2.00+ 2.3.4.4, no splitting up fid tag */
+ if (dir_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ /* pre-grow directory to see if we're to switch */
+ // udf_grow_node(dir_node, dir_size + chosen_size);
+ error = udf_truncate_node(dir_node, chosen_fid_pos + chosen_size);
+ assert(!error);
+ }
+
+ /* make sure the next fid desc_tag won't be splitted */
+ if (dir_node->addr_type != UDF_ICB_INTERN_ALLOC) {
+ end_fid_pos = chosen_fid_pos + chosen_size;
+ lb_rest = lb_size - (end_fid_pos % lb_size);
+
+ /* pad with implementation use regid if needed */
+ if (lb_rest < sizeof(struct desc_tag))
+ chosen_size += 32;
+ }
+ }
+ chosen_size_diff = chosen_size - fidsize;
+
+ /* populate the FID */
+ memset(fid, 0, lb_size);
+ udf_init_desc_tag(&fid->tag, TAGID_FID, descr_ver, 1); /* tag serial number */
+ fid->file_version_num = i_fid->file_version_num;
+ fid->file_char = i_fid->file_char;
+ fid->icb = i_fid->icb;
+ fid->l_iu = udf_rw16(0);
+
+ if (chosen_size > fidsize) {
+ /* insert implementation-use regid to space it correctly */
+ fid->l_iu = udf_rw16(chosen_size_diff);
+
+ /* set implementation use */
+ udf_set_imp_id((struct regid *) fid->data);
+ }
+
+ /* copy name */
+ fid->l_fi = i_fid->l_fi;
+ memcpy(fid->data + udf_rw16(fid->l_iu), i_fid->data, fid->l_fi);
+
+ fid->tag.desc_crc_len = chosen_size - UDF_DESC_TAG_LENGTH;
+
+ /* writeout modified piece */
+ udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+ error = udf_writeout_fid_info(dir_node, fid, chosen_fid_pos, chosen_size);
+ assert(!error);
+
+ /* append to the dirhash */
+ fid_name = (char *) fid->data + udf_rw16(fid->l_iu);
+ dirent.d_fileno = udf_rw32(fid->icb.impl.im_used.unique_id); /* only 32 bits salvageable */
+#if !defined(__DragonFly__)
+ dirent.d_reclen = sizeof(struct dirent);
+#endif
+ dirent.d_type = DT_UNKNOWN;
+ udf_to_unix_name(dirent.d_name, fid_name, fid->l_fi, &dir_node->udf_log_vol->log_vol->desc_charset);
+#ifndef NO_DIRENT_NAMLEN
+ dirent.d_namlen = strlen(dirent.d_name);
+#endif
+
+ if (fid->file_char & UDF_FILE_CHAR_DIR) dirent.d_type = DT_DIR;
+ if (fid->file_char & UDF_FILE_CHAR_PAR) strcpy(dirent.d_name, "..");
+
+ dirhash_enter(dirh, &dirent, chosen_fid_pos, udf_fidsize(fid), 1);
+
+ free(fid);
+ dirhash_put(dir_node->dir_hash);
+
+ return error;
+}
+
+
+/* create a file in the given directory with the given name and attributes using udf's file_char and udf'd filetype */
+/* note
+ * 1) that with `refering' node specified its effectively `link()'
+ * 2) that with `refering' node specified, `filetype' is discarded as it ought to be the same as the `refering' one
+ *
+ * XXX this function needs to be splitted into node creation and directory
+ * attachment; its now doing both in one go.
+ */
+int udf_create_directory_entry(struct udf_node *dir_node, char *name, int filetype, int filechar, struct udf_node *refering, struct stat *stat, struct udf_node **new_node) {
+ struct udf_allocentry *alloc_entry;
+ struct udf_log_vol *udf_log_vol;
+ struct udf_node *udf_node;
+ struct charspec osta_charspec;
+ struct fileid_desc *fid;
+ struct long_ad icb_loc;
+ uint32_t lb_num, lb_size;
+ uint16_t vpart_num, descr_ver, len;
+ int found, error;
+
+ assert(dir_node);
+ assert(name);
+ assert(dir_node->udf_log_vol);
+ udf_log_vol = dir_node->udf_log_vol;
+ lb_size = udf_log_vol->lb_size;
+ descr_ver = udf_rw16(udf_log_vol->log_vol->tag.descriptor_ver);
+
+ *new_node = NULL;
+
+ /* lookup if it allready exists (sanity mainly) */
+ fid = malloc(lb_size);
+ assert(fid);
+
+ error = udf_lookup_name_in_dir(dir_node, name, strlen(name), &icb_loc, fid, &found);
+ if (!error && found) {
+ /* it existed! allready there */
+ free(fid);
+ return EEXIST;
+ }
+
+ if (!refering) {
+ /*
+ * Get ourselves an empty node and space to record file
+ * descriptor in.
+ */
+ error = udf_init_udf_node(dir_node->mountpoint, udf_log_vol, "New direntry", &udf_node);
+ if (error) {
+ free(fid);
+ return error;
+ }
+
+ udf_node->udf_filetype = filetype;
+ udf_node->udf_filechar = filechar;
+ udf_node->unique_id = udf_increment_unique_id(udf_log_vol);
+
+ /* snif */
+ error = udf_allocate_udf_node_on_disc(udf_node);
+ if (error) {
+ assert(udf_node != dir_node);
+ udf_dispose_udf_node(udf_node);
+ free(fid);
+ return error;
+ }
+
+ udf_node->stat = *stat;
+ /* note passed creation times; do sanitise them */
+#ifndef NO_STAT_BIRTHTIME
+ if (udf_insanetimespec(&stat->st_birthtimespec))
+ udf_set_timespec_now(&udf_node->stat.st_birthtimespec);
+#endif
+ if (udf_insanetimespec(&stat->st_ctimespec))
+ udf_set_timespec_now(&udf_node->stat.st_ctimespec);
+ if (udf_insanetimespec(&stat->st_atimespec))
+ udf_set_timespec_now(&udf_node->stat.st_atimespec);
+ if (udf_insanetimespec(&stat->st_mtimespec))
+ udf_set_timespec_now(&udf_node->stat.st_mtimespec);
+ } else {
+ /* refering->ignore passed stat info */
+ udf_node = refering;
+ filetype = udf_node->udf_filetype;
+
+ /* linking changes metadata modification */
+ udf_set_timespec_now(&udf_node->stat.st_ctimespec);
+ }
+ alloc_entry = TAILQ_FIRST(&udf_node->dscr_allocs);
+ vpart_num = alloc_entry->vpart_num;
+ lb_num = alloc_entry->lb_num;
+
+ /* build up new directory entry */
+ memset(fid, 0, lb_size);
+ udf_osta_charset(&osta_charspec);
+ udf_init_desc_tag(&fid->tag, TAGID_FID, descr_ver, 1); /* tag serial number */
+
+ if (filechar & UDF_FILE_CHAR_PAR) {
+ /* parent or `..' is not allowed to have a name length ... wierd but ok */
+ fid->l_fi = 0;
+ } else {
+ unix_to_udf_name((char *) fid->data, name, &fid->l_fi, &osta_charspec);
+ }
+ fid->file_version_num = udf_rw16(1); /* new file/dir; version starts at 1 */
+ fid->file_char = filechar; /* what is it */
+ fid->l_iu = udf_rw32(0); /* no impl. use */
+ fid->icb.len = udf_rw32(lb_size); /* fill in location */
+ fid->icb.loc.part_num = udf_rw16(vpart_num);
+ fid->icb.loc.lb_num = udf_rw32(lb_num);
+
+ /* fill in lower 32 bits of unique ID (UDF 3/3.2.2.1) in the impl use part of the FID's long_ad */
+ fid->icb.impl.im_used.unique_id = udf_rw32(((udf_node->unique_id << 32) >> 32));
+
+ /* calculate minimum size needed for directory entry */
+ len = UDF_FID_SIZE + fid->l_fi;
+ len = (len + 3) & ~3;
+ fid->tag.desc_crc_len = udf_rw16(len - UDF_DESC_TAG_LENGTH);
+
+ error = udf_insert_fid_info(dir_node, udf_node, fid, len);
+ free(fid); /* Ahum... easily forgotten here */
+
+ if (error) {
+ fprintf(stderr, "UDF: fid insertion failed : %s\n", strerror(error));
+ if (!refering)
+ udf_dispose_udf_node(udf_node);
+ return error;
+ }
+
+ if (udf_node) {
+ /* only insert file in hashlist if its not an explicit reference */
+ if (!refering) {
+ udf_insert_node_in_hash(udf_node);
+ } else {
+ refering->link_cnt++;
+ udf_node_mark_dirty(refering);
+ }
+ udf_node_mark_dirty(udf_node);
+ }
+
+ *new_node = udf_node;
+ return error;
+}
+
+
+/*
+ * Rename file from old_name to new_name. `present' is the file to be replaced
+ * if found present allready. Care should be taken that the directory tree is
+ * kept intact. To prevent this no path should be possible from the new parent
+ * to the node to be renamed if it considers a directory and the new_parent is
+ * not equal to the old parent.
+ */
+/*
+ * VOP_RENAME(struct vnode *fdvp, struct vnode *vp,
+ * struct componentname *fcnp, struct componentname *tdvp,
+ * struct vnode *tvp, struct componentname *tcnp
+ * );
+ */
+int udf_rename(struct udf_node *old_parent, struct udf_node *rename_me, char *old_name, struct udf_node *new_parent, struct udf_node *present, char *new_name) {
+ struct udf_node *new_node;
+ int error;
+
+ /* sanity */
+ if (!old_parent) return ENOENT;
+ if (!new_parent) return ENOENT;
+ if (!rename_me) return ENOENT;
+ if (!(old_parent->stat.st_mode & S_IFDIR)) return ENOTDIR;
+ if (!(new_parent->stat.st_mode & S_IFDIR)) return ENOTDIR;
+
+ if (udf_open_logvol(old_parent->udf_log_vol))
+ return EROFS;
+
+ if (udf_open_logvol(new_parent->udf_log_vol))
+ return EROFS;
+
+ if ((present && (present->stat.st_mode & S_IFDIR)) || (old_parent != new_parent)) {
+ /* cross directory moves */
+ fprintf(stderr, "Cross directory renaming is not implemented yet.\n");
+ return ENOTSUP;
+ }
+
+ /* if it was present, delete old contents; reference counting is done */
+ if (present) {
+ /* TODO what about non dir, non file entries? */
+ if (present->stat.st_mode & S_IFDIR) {
+ error = udf_remove_directory(new_parent, present, new_name);
+ } else {
+ error = udf_remove_file(new_parent, present, new_name);
+ }
+ if (error)
+ return error;
+ }
+
+ /* insert new_name HARD-linked to the `rename_me' node */
+ error = udf_create_directory_entry(new_parent, new_name, rename_me->udf_filetype, rename_me->udf_filechar, rename_me, NULL, &new_node);
+ if (error) return error;
+
+ /* extra sanity */
+ if (!new_node) return ENOENT;
+
+ /* 3) remove old link and mark directories dirty */
+ error = udf_remove_directory_entry(old_parent, rename_me, old_name);
+ udf_node_mark_dirty(old_parent);
+ udf_node_mark_dirty(new_parent);
+
+ return error;
+}
+
+
+/* VOP_CREATE */
+int udf_create_file(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) {
+ struct udf_log_vol *udf_log_vol;
+ struct udf_node *udf_node;
+ uint32_t lb_size;
+ int error;
+
+ if (!dir_node) return EINVAL;
+
+ udf_log_vol = dir_node->udf_log_vol;
+ if (!udf_log_vol) return EINVAL;
+
+ lb_size = udf_log_vol->lb_size;
+ if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, lb_size))
+ return ENOSPC;
+
+ if (udf_open_logvol(dir_node->udf_log_vol))
+ return EROFS;
+
+ error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_RANDOMACCESS, 0, NULL, stat, new_node);
+ if ((!error) && (*new_node)) {
+ udf_node = *new_node;
+ /* update sizes */
+ udf_node->stat.st_size = 0;
+ udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size;
+ udf_node->stat.st_blocks = 0; /* not 1? */
+
+ udf_node->udf_log_vol->num_files++;
+
+ udf_node_mark_dirty(udf_node);
+ }
+ return error;
+}
+
+
+/* VOP_MKDIR */
+int udf_create_directory(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node) {
+ struct udf_log_vol *udf_log_vol;
+ struct udf_node *udf_node, *dummy_node;
+ uint32_t lb_size;
+ int error;
+
+ if (!dir_node) return EINVAL;
+
+ udf_log_vol = dir_node->udf_log_vol;
+ if (!udf_log_vol) return EINVAL;
+
+ lb_size = udf_log_vol->lb_size;
+ if (!udf_confirm_freespace(udf_log_vol, UDF_C_NODE, 2*lb_size))
+ return ENOSPC;
+
+ if (udf_open_logvol(dir_node->udf_log_vol))
+ return EROFS;
+
+ stat->st_mode |= S_IFDIR;
+ error = udf_create_directory_entry(dir_node, componentname, UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR, NULL, stat, new_node);
+ if ((!error) && (*new_node)) {
+ udf_node = *new_node;
+ /* update sizes */
+ udf_node->stat.st_size = 0;
+ udf_node->stat.st_blksize = dir_node->udf_log_vol->lb_size;
+ udf_node->stat.st_blocks = 0; /* not 1? */
+
+ udf_node->udf_log_vol->num_directories++;
+
+ udf_node_mark_dirty(udf_node);
+
+ /* create `..' directory entry */
+ error = udf_create_directory_entry(udf_node, "..", UDF_ICB_FILETYPE_DIRECTORY, UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR, dir_node, stat, &dummy_node);
+ if (error) {
+ /* use of _prim for dir counting might not go well due to aborted creation */
+ error = udf_remove_directory_prim(dir_node, udf_node, componentname);
+ }
+ }
+ return error;
+}
+
+
+
+/* really deletes all space referenced to this udf node including descriptor spaces and removes it from the administration */
+int udf_unlink_node(struct udf_node *udf_node) {
+ struct udf_allocentry *alloc_entry;
+ uint32_t lbnum, len;
+ uint16_t vpart;
+ int error, flags;
+
+ /* just in case its called from outside */
+ if (udf_open_logvol(udf_node->udf_log_vol))
+ return EROFS;
+
+ /* unlinking changes metadata modification */
+ udf_set_timespec_now(&udf_node->stat.st_ctimespec);
+
+ udf_node->link_cnt--;
+ udf_node_mark_dirty(udf_node);
+ if (udf_node->link_cnt > 0)
+ return 0;
+
+ /* trunc node */
+ udf_truncate_node(udf_node, (uint64_t) 0); /* get rid of file contents */
+
+ /* free descriptors from dscr_allocs queue */
+ TAILQ_FOREACH(alloc_entry, &udf_node->dscr_allocs, next_alloc) {
+ vpart = alloc_entry->vpart_num;
+ lbnum = alloc_entry->lb_num;
+ flags = alloc_entry->flags;
+ len = alloc_entry->len;
+
+ error = udf_release_lbs(udf_node->udf_log_vol, vpart, lbnum, len);
+ /* what if an error occures? */
+ assert(error == 0);
+ }
+
+ /* delete from administration */
+ udf_dispose_udf_node(udf_node);
+
+ return 0;
+}
+
+
+/* NOTE: Dont use the EXTENT erased part; its for non sequential WORM only */
+/* UDF 2.3.10.1, ECMA 4/48.14.1.1 */
+/* fid->icb.impl.im_used.flags = udf_rw16(UDF_ADIMP_FLAGS_EXTENT_ERASED); */
+
+static int udf_remove_directory_entry(struct udf_node *dir_node, struct udf_node *udf_node, char *name) {
+ struct dirhash *dirh;
+ struct dirhash_entry *dirh_ep;
+ struct fileid_desc *fid;
+ struct dirent *dirent;
+ uint64_t file_size, diroffset;
+ uint32_t lb_size, namelen, fidsize;
+ int hit, found;
+ int error;
+
+ assert(dir_node);
+ assert(udf_node);
+ assert(udf_node->udf_log_vol);
+ assert(name);
+
+ namelen = strlen(name);
+ if (strncmp(name, "..", 3) == 0) {
+ printf("Asked to remove `..' parent directory identifier; not allowed!\n");
+ return ENOENT;
+ }
+
+ if (strncmp(name, ".", 2) == 0) {
+ printf("Asked to remove `.' current directory identifier; not allowed!\n");
+ return ENOENT;
+ }
+
+ /* only lookup in directories XXX DT_COMP also possible XXX */
+ if ((dir_node->stat.st_mode & S_IFDIR) == 0)
+ return ENOTDIR;
+
+ /* get our dirhash and make sure its read in */
+ dirhash_get(&dir_node->dir_hash);
+ error = dirhash_fill(dir_node);
+ if (error) {
+ dirhash_put(dir_node->dir_hash);
+ return error;
+ }
+ dirh = dir_node->dir_hash;
+
+ /* get directory filesize */
+ file_size = dir_node->stat.st_size;
+
+ /* allocate temporary space for fid */
+ lb_size = udf_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ dirent = malloc(sizeof(struct dirent));
+ if (!fid || !dirent) {
+ error = ENOMEM;
+ goto error_out;
+ }
+
+ /* search our dirhash hits */
+ found = 0;
+ dirh_ep = NULL;
+ for (;;) {
+ hit = dirhash_lookup(dirh, name, namelen, &dirh_ep);
+ /* if no hit, abort the search */
+ if (!hit)
+ break;
+
+ /* check this hit */
+ diroffset = dirh_ep->offset;
+
+ /* transfer a new fid/dirent */
+ error = udf_read_fid_stream(dir_node, &diroffset, fid, dirent);
+ if (error)
+ break;
+
+ /* see if its our entry */
+ assert(DIRENT_NAMLEN(dirent) == namelen);
+ if (strncmp(dirent->d_name, name, namelen) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ error = ENOENT;
+ if (error)
+ goto error_out;
+
+ /* get size of fid and compensate for the read_fid_stream advance */
+ fidsize = udf_fidsize(fid);
+ diroffset -= fidsize;
+
+ /* mark fid as deleted */
+ fid->file_char |= UDF_FILE_CHAR_DEL;
+ bzero(&fid->icb, sizeof(struct long_ad));
+ udf_validate_tag_and_crc_sums((union dscrptr *) fid);
+ udf_writeout_fid_info(dir_node, fid, diroffset, fidsize);
+
+ /* remove from the dirhash */
+ dirhash_mark_freed(dirh, dirh_ep, dirent);
+
+ /* delete node and its administration if refcount indicates so */
+ udf_unlink_node(udf_node);
+
+error_out:
+ if (fid)
+ free(fid);
+ if (dirent)
+ free(dirent);
+
+ dirhash_put(dir_node->dir_hash);
+
+ return error;
+}
+
+
+
+/* VOP_REMOVE */
+int udf_remove_file(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) {
+ int error;
+
+ if (udf_open_logvol(dir_node->udf_log_vol))
+ return EROFS;
+
+ if (udf_node->stat.st_mode & S_IFDIR) {
+ /* only remove files with this call */
+ return EISDIR;
+ }
+
+ error = udf_remove_directory_entry(dir_node, udf_node, componentname);
+ if (!error) {
+ dir_node->udf_log_vol->num_files--;
+ } /* else? */
+
+ return error;
+}
+
+
+static int udf_remove_directory_prim(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) {
+ int error;
+
+ if (udf_open_logvol(dir_node->udf_log_vol))
+ return EROFS;
+
+ /* remove the entry */
+ error = udf_remove_directory_entry(dir_node, udf_node, componentname);
+ if (!error) {
+ dir_node->link_cnt--;
+ udf_node_mark_dirty(dir_node);
+ dir_node->udf_log_vol->num_directories--;
+ } else {
+ /* whoah! something went wrong, mark the .. as present again */
+ printf("UDF warning: filesystem might by in compromised state\n");
+ assert(udf_node);
+ udf_node->link_cnt++;
+ }
+
+ return error;
+}
+
+
+/* VOP_RMDIR */
+int udf_remove_directory(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname) {
+ uint32_t num_nodes;
+ int error;
+
+ if (!(udf_node->stat.st_mode & S_IFDIR)) {
+ /* only remove directories with this call */
+ return ENOTDIR;
+ }
+
+ error = udf_count_direntries(udf_node, 0, &num_nodes);
+ if (error) return error;
+
+ if (num_nodes != 0) return ENOTEMPTY;
+
+ error = udf_remove_directory_prim(dir_node, udf_node, componentname);
+
+ return error;
+}
+
+
+/* end of udf.c */
+
diff --git a/udf.h b/udf.h
new file mode 100644
index 0000000..f8cfad0
--- /dev/null
+++ b/udf.h
@@ -0,0 +1,628 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf.h" is part of the UDFclient toolkit.
+ * File $Id: udf.h,v 1.149 2015/08/05 18:26:30 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef _UDF_H_
+#define _UDF_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include "dirhash.h"
+
+#ifdef NO_INT_FMTIO
+ /* assume 32 bits :-/ */
+#ifndef PRIu32
+# define PRIu32 "u"
+# define PRIx32 "x"
+# define PRIu64 "lld"
+# define PRIx64 "llx"
+#endif
+#endif
+
+
+/* exported flags */
+extern int udf_verbose;
+
+#define UDF_MNT_RDONLY 1
+#define UDF_MNT_FORCE 2
+#define UDF_MNT_BSWAP 4
+
+#define UDF_VERBLEV_NONE 0
+#define UDF_VERBLEV_ACTIONS 1
+#define UDF_VERBLEV_TABLES 2
+#define UDF_VERBLEV_MAX 3
+
+/* constants to identify what kind of identifier we are dealing with */
+#define UDF_REGID_DOMAIN 1
+#define UDF_REGID_UDF 2
+#define UDF_REGID_IMPLEMENTATION 3
+#define UDF_REGID_APPLICATION 4
+#define UDF_REGID_NAME 99 /* 99? */
+
+/* RW content hint for allocation and other purposes */
+#define UDF_C_DSCR 0
+#define UDF_C_USERDATA 1
+#define UDF_C_FIDS 2
+#define UDF_C_NODE 3
+
+
+
+/* Configuration */
+
+#define UDF_MINFREE_LOGVOL (128*1024) /* max sector is 64kb, give 2 (sorry) slack; use with care */
+#define UDF_LOOKUP_READAHEAD 64 /* rewrite lookahead; set lookahead 0 to disable */
+
+/* misc. configuration; don't change unless you know what you're doing */
+#define UDF_READWRITE_LINE_LENGTH 32 /* DONT change this! 32 sectors for CD-RW/DVD-RW fixed packets */
+#define UDF_READWRITE_ALL_PRESENT 0xffffffff
+
+#define UDF_INODE_HASHBITS 10
+#define UDF_INODE_HASHSIZE (1<<UDF_INODE_HASHBITS)
+#define UDF_INODE_HASHMASK (UDF_INODE_HASHSIZE - 1)
+
+#define UDF_BUFCACHE_HASHBITS 13
+#define UDF_BUFCACHE_HASHSIZE (1<<UDF_BUFCACHE_HASHBITS)
+#define UDF_BUFCACHE_HASHMASK (UDF_BUFCACHE_HASHSIZE - 1)
+
+#define UDF_BUFCACHE_IDLE_SECS 15 /* chosen... but preferably before the device spins down (!) */
+#define UDF_LRU_METADATA_MIN (100 * UDF_READWRITE_LINE_LENGTH)
+#define UDF_LRU_METADATA_MAX (150 * UDF_READWRITE_LINE_LENGTH)
+#define UDF_LRU_DATA_MIN (100 * UDF_READWRITE_LINE_LENGTH)
+#define UDF_LRU_DATA_MAX (300 * UDF_READWRITE_LINE_LENGTH)
+
+/* end of Configuration */
+
+
+#if 1
+# define UDF_VERBOSE(op) if (udf_verbose) { op; }
+# define UDF_VERBOSE_LEVEL(level, op) if (udf_verbose >= (level)) { op; }
+# define UDF_VERBOSE_TABLES(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_TABLES, op)
+# define UDF_VERBOSE_MAX(op) UDF_VERBOSE_LEVEL(UDF_VERBLEV_MAX, op)
+#else
+# define UDF_VERBOSE(op)
+# define UDF_VERBOSE_LEVEL(level, op)
+# define UDF_VERBOSE_TABLES(op)
+# define UDF_VERBOSE_MAX(op)
+#endif
+
+
+#define UDF_MUTEX(name) struct { \
+ pthread_mutex_t mutex;\
+ int locked;\
+ char *status;\
+ char *file;\
+ int line;\
+ } name
+
+#define UDF_MUTEX_INIT(name) { \
+ pthread_mutex_init(&(name)->mutex, 0); \
+ (name)->locked = 0; \
+ (name)->status = "initialised as " #name;\
+ (name)->file = __FILE__;\
+ (name)->line = __LINE__;\
+ }
+
+#define UDF_MUTEX_LOCK(name) { \
+ if (0 && (name)->locked) printf("Waiting for lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\
+ pthread_mutex_lock(&(name)->mutex);\
+ (name)->locked = 1; \
+ (name)->status = "locked as " #name;\
+ (name)->file = __FILE__;\
+ (name)->line = __LINE__;\
+ }
+
+#define UDF_MUTEX_TRYLOCK(name) { \
+ if (0) printf("Trying lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\
+ if (pthread_mutex_trylock(&(name)->mutex) != EBUSY) {\
+ (name)->locked = 1;\
+ (name)->status = "locked as " #name;\
+ (name)->file = __FILE__;\
+ (name)->line = __LINE__;\
+ };\
+ }
+
+#define UDF_MUTEX_UNLOCK(name) { \
+ if (0 && !(name)->locked) printf("Unlocking lock " #name " at line %d of file %s marked %s in %s at line %d\n", __LINE__, __FILE__, (name)->status, (name)->file, (name)->line);\
+ (name)->locked = 0; \
+ (name)->status = "unlocked as " #name;\
+ (name)->file = __FILE__;\
+ (name)->line = __LINE__;\
+ pthread_mutex_unlock(&(name)->mutex);\
+ }
+
+
+/* Predefines */
+struct udf_node;
+struct udf_mountpoint;
+
+#include "osta.h"
+#include "ecma167-udf.h"
+#include "queue.h"
+
+#include "udf_discop.h"
+#include "udf_unix.h"
+#include "uio.h"
+#include <pthread.h>
+
+
+/*---------------------------------------------------------------------*/
+
+/*
+ * Provides :: write action -> signal written OK or signal write error
+ * NOTE: not used anymore but kept for convenience when write error resolution
+ * is build.
+ */
+struct udf_wrcallback;
+typedef void (udf_wrcallback_func)(int reason, struct udf_wrcallback *wrcallback, int error, uint8_t *sectordata);
+
+struct udf_wrcallback {
+ udf_wrcallback_func *function;
+ struct udf_buf *udf_buf; /* associated buffer */
+ struct udf_node *udf_node; /* associated vnode */
+ uint32_t flags; /* some reserved flags but other bits are OK */
+};
+#define UDF_WRCALLBACK_REASON_PENDING 0
+#define UDF_WRCALLBACK_REASON_ANULATE 1
+#define UDF_WRCALLBACK_REASON_WRITTEN 2
+#define UDF_WRCALLBACK_FLAG_DESCRIPTOR (1<<0)
+#define UDF_WRCALLBACK_FLAG_BUFFER (1<<1)
+
+
+/*
+ * Provides :: ``allocentry'' an ordered list of allocation extents
+ *
+ */
+
+struct udf_allocentry {
+ uint32_t len;
+ uint32_t lb_num;
+ uint16_t vpart_num;
+ uint8_t flags;
+
+ TAILQ_ENTRY(udf_allocentry) next_alloc;
+};
+#define UDF_SPACE_ALLOCATED 0
+#define UDF_SPACE_FREED 1
+#define UDF_SPACE_ALLOCATED_BUT_NOT_USED 1
+#define UDF_SPACE_FREE 2
+#define UDF_SPACE_REDIRECT 3
+
+
+TAILQ_HEAD(udf_alloc_entries, udf_allocentry);
+
+
+/*
+ * Provides :: ``inode'' or rather filing system dependent v_data node linked under vnode structure
+ *
+ */
+
+struct udf_node {
+ struct udf_mountpoint *mountpoint; /* foreign; provides logvol and fileset */
+ struct udf_log_vol *udf_log_vol; /* foreign; backup if its not associated to mountpoint */
+
+ int dirty; /* flags if (ext)fentry needs to be synced (safeguard) */
+ int hold; /* node is on hold i.e. don't recycle nor its buffers */
+
+ ino_t hashkey; /* for hashing to lookup inode by vpart & lbnum */
+ struct stat stat; /* holds unix file attributes/filestats */
+
+ struct udf_alloc_entries dscr_allocs; /* where my complete descriptor space is located */
+
+ uint8_t udf_filetype; /* filetype of this node; raw, block, char, softlink... */
+ uint8_t udf_filechar; /* udf file characteristics (vis, meta, ... */
+ uint16_t serial_num; /* serial number of this descriptor on disc */
+ uint16_t file_version_num; /* file version number of this descriptor on disc */
+
+ uint16_t udf_icbtag_flags; /* serial, setuid, setguid, stickybit etc */
+ uint16_t link_cnt; /* how many FID's are linked to this (ext)fentry */
+ uint64_t unique_id; /* unique file ID */
+
+ /* extended attributes and subfiles */
+ struct udf_alloc_entry *extattrfile_icb; /* associated extended attribute file */
+ struct udf_alloc_entry *streamdir_icb; /* associated streamdir */
+
+ /* internal in-node storage of extended attributes copy. */
+ uint8_t *extended_attr;
+ uint32_t extended_attr_len;
+
+ /* internal in-node storage of data copy */
+ uint8_t *intern_data; /* descriptor internal data */
+ uint32_t intern_len; /* length of descriptor internal data */
+ uint32_t intern_free; /* free space amount in the descriptor besides extattr. */
+
+ /* extents of discspace that make up this file/directory */
+ uint32_t addr_type; /* storage type of allocation descriptors */
+ uint32_t icb_len; /* length of an icb descriptor */
+ UDF_MUTEX(alloc_mutex);
+ struct udf_alloc_entries alloc_entries;
+
+ /* all associated vn_bufs for this node */
+ UDF_MUTEX(buf_mutex);
+ struct udf_buf_queue vn_bufs;
+
+ uint32_t v_numoutput;
+
+ /* dir hasing */
+ struct dirhash *dir_hash;
+
+ /* lists */
+ TAILQ_ENTRY(udf_node) next_dirty; /* next in dirty node list */
+ LIST_ENTRY(udf_node) next_node; /* next in hash node list */
+};
+
+TAILQ_HEAD(udf_node_list, udf_node);
+
+
+/*---------------------------------------------------------------------*/
+
+
+/*
+ * Provides :: mountpoint -> fileset descriptor and logical volume
+ *
+ */
+struct udf_mountpoint {
+ char *mount_name; /* identifier */
+ struct udf_log_vol *udf_log_vol; /* foreign */
+ struct fileset_desc *fileset_desc; /* fileset belonging to this mountpoint */
+
+ struct udf_node *rootdir_node;
+ struct udf_node *streamdir_node;
+
+ int writable; /* flags if its a writable fileset */
+
+ SLIST_ENTRY(udf_mountpoint) all_next; /* for overall mountpoint list */
+ SLIST_ENTRY(udf_mountpoint) logvol_next; /* for list of mountpoints in a logvol */
+};
+
+
+/*
+ * Provides :: logvol_partition -> volumeset physical partition
+ */
+struct udf_part_mapping {
+ uint32_t udf_part_mapping_type;
+ uint32_t vol_seq_num;
+ uint32_t udf_virt_part_num;
+ uint32_t udf_phys_part_num;
+ union udf_pmap *udf_pmap; /* foreign */
+
+ int data_writable; /* flags if its suited for data */
+ int metadata_writable; /* flags if its for meta-data */
+
+ /* supporting tables */
+ struct udf_sparing_table *sparing_table;
+
+ /* virtual space */
+ struct udf_node *vat_udf_node;
+ struct udf_vat *vat;
+ uint8_t *vat_translation;
+ uint32_t vat_entries;
+ uint32_t vat_length;
+
+ /* needs to be updated; metadata partition disabled for now */
+ struct udf_node *meta_file;
+ struct udf_node *meta_mirror_file;
+ struct udf_node *meta_bitmap_file;
+
+ SLIST_ENTRY(udf_part_mapping) next_mapping; /* for list of partition mappings */
+};
+#define UDF_PART_MAPPING_ERROR 0
+#define UDF_PART_MAPPING_PHYSICAL 1
+#define UDF_PART_MAPPING_VIRTUAL 2
+#define UDF_PART_MAPPING_SPARABLE 3
+#define UDF_PART_MAPPING_META 4
+#define UDF_PART_MAPPING_PSEUDO_RW 5
+
+
+/*
+ * Provides :: log_vol -> ...
+ * :: MOUNTPOINTS :)
+ */
+struct udf_log_vol {
+ int broken;
+
+ /* primary volume this logical volume is recorded on */
+ struct udf_pri_vol *primary; /* foreign */
+
+ /* logical volume info */
+ struct logvol_desc *log_vol;
+ uint32_t lb_size; /* constant over logvol in Ecma 167 */
+ uint32_t sector_size; /* constant over logvol in Ecma 167 */
+
+ /* logical volume integrity/VAT information */
+ uint32_t logvol_state; /* maintained */
+ uint16_t integrity_serial;
+ uint32_t min_udf_readver;
+ uint32_t min_udf_writever;
+ uint32_t max_udf_writever;
+ uint32_t num_files; /* maintained */
+ uint32_t num_directories; /* maintained */
+ uint64_t next_unique_id; /* maintained */
+
+ int writable; /* flags if its writable */
+
+ /* dirty nodes administration */
+ UDF_MUTEX(dirty_nodes_mutex);
+ struct udf_node_list dirty_nodes;
+
+ /* hash table to lookup ino_t -> udf_node */
+ LIST_HEAD(inodes, udf_node) udf_nodes[UDF_INODE_HASHSIZE];
+
+ /* estimated free space summation; from logvol integrity */
+ uint64_t total_space;
+ uint64_t free_space;
+ uint64_t await_alloc_space;
+
+ /* consisting of */
+ uint32_t data_vpart, metadata_vpart;
+
+ uint32_t num_mountpoints; /* display only */
+ SLIST_HEAD(mountpoints_list, udf_mountpoint) mountpoints; /* list of mountables in logvol */
+
+ uint32_t num_part_mappings; /* display only */
+ SLIST_HEAD(part_mappings_list, udf_part_mapping) part_mappings; /* list of partition mappings */
+
+ /* next in list */
+ SLIST_ENTRY(udf_log_vol) next_logvol; /* for list of logical volumes in a primary volume */
+};
+
+
+/*
+ * Provides :: pri_vol -> [log_vol], [part],[ ...]
+ * :: { volumeset -> [pri_vols] }
+ */
+struct udf_pri_vol {
+ struct pri_vol_desc *pri_vol;
+ struct udf_session *udf_session;
+
+ struct impvol_desc *implemation; /* most likely reduntant */
+ struct udf_volumeset *volumeset; /* foreign ; nesissary? */
+ struct unalloc_sp_desc *unallocated;
+
+ /* associated logical volumes */
+ SLIST_HEAD(logvols, udf_log_vol) log_vols; /* list of associated logical volumes */
+
+ STAILQ_ENTRY(udf_pri_vol) next_primary; /* for primary list in volumeset */
+};
+
+
+/*
+ * Provides :: partion -> [partition info, session]
+ */
+struct udf_partition {
+ struct part_desc *partition;
+ struct udf_session *udf_session; /* foreign */
+
+ uint64_t part_offset;
+ uint64_t part_length;
+
+ UDF_MUTEX(partition_space_mutex); /* MUTEX for unalloc and freed space */
+ uint64_t free_unalloc_space;
+ struct udf_alloc_entries unalloc_space_queue; /* authorative */
+ struct space_bitmap_desc *unalloc_space_bitmap; /* placeholder! does NOT have to be up-to-date */
+
+ uint64_t free_freed_space;
+ struct udf_alloc_entries freed_space_queue; /* authorative */
+ struct space_bitmap_desc *freed_space_bitmap; /* placeholder! does NOT have to be up-to-date */
+
+ SLIST_ENTRY(udf_partition) next_partition; /* for partition list in volumeset */
+};
+
+
+/*
+ * Provides :: volumeset -> [pri_vol]
+ * :: [volumeset]
+ */
+struct udf_volumeset {
+ int obsolete;
+ uint32_t max_partnum;
+
+ STAILQ_HEAD(primaries, udf_pri_vol) primaries; /* linked list of primary volumes associated */
+ SLIST_HEAD(parts, udf_partition) parts; /* linked list of partitions descriptors */
+
+ SLIST_ENTRY(udf_volumeset) next_volumeset; /* for volumeset list */
+};
+
+
+/*
+ * Provides udf_session :: -> [(disc, anchor, tracknum)]
+ */
+struct udf_session {
+ struct udf_discinfo *disc;
+ struct anchor_vdp anchor;
+
+ uint16_t session_num;
+ uint32_t session_offset;
+ uint32_t session_length;
+
+ int writable;
+
+ /* physical layer read/write cache */
+ UDF_MUTEX(session_cache_lock);
+
+ /* SIMPLE cache */
+ uint32_t cache_line_r_start;
+ uint32_t cache_line_r_present;
+ uint8_t *cache_line_read;
+
+ uint32_t cache_line_w_start;
+ uint32_t cache_line_w_present;
+ uint32_t cache_line_w_dirty;
+ uint8_t *cache_line_write;
+
+ struct udf_wrcallback cache_write_callbacks[UDF_READWRITE_LINE_LENGTH+1];
+
+ STAILQ_ENTRY(udf_session) next_session; /* sessions are added at tail to preserve order */
+};
+/*---------------------------------------------------------------------*/
+
+
+/* exported functions */
+extern int udf_check_tag(union dscrptr *dscr);
+extern int udf_check_tag_payload(union dscrptr *dscr);
+extern int udf_check_tag_presence(union dscrptr *dscr, int TAG);
+
+extern int udf_check_session_range(char *range);
+
+
+/* XXX new kernel like interface XXX */
+extern int udf_read_file_part_uio(struct udf_node *udf_node, char *what, int cachehints, struct uio *data_uio);
+extern int udf_write_file_part_uio(struct udf_node *udf_node, char *what, int cachehints, struct uio *data_uio);
+extern void udf_dispose_udf_node(struct udf_node *udf_node);
+extern int udf_getattr(struct udf_node *udf_node, struct stat *stat);
+extern int udf_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res /* int *cookies, int ncookies */);
+//extern int udf_lookup_name_in_dir(struct udf_node *dir_node, struct udf_node **vnode, char *name); /* not fully VOP_LOOKUP yet */
+extern int udf_lookup_name_in_dir(struct udf_node *dir_node, char *name, int namelen, struct long_ad *icb_loc, struct fileid_desc *fid, int *found);
+extern int udf_readin_udf_node(struct udf_node *dir_node, struct long_ad *udf_icbptr, struct fileid_desc *fid, struct udf_node **res_sub_node);
+extern int udf_sync_udf_node(struct udf_node *udf_node, char *why); /* writeout node */
+extern int udf_truncate_node(struct udf_node *udf_node, uint64_t length /* ,ioflags */);
+extern int udf_remove_file(struct udf_node *parent_node, struct udf_node *udf_node, char *componentname);
+extern int udf_remove_directory(struct udf_node *dir_node, struct udf_node *udf_node, char *componentname);
+extern int udf_create_file(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node);
+extern int udf_create_directory(struct udf_node *dir_node, char *componentname, struct stat *stat, struct udf_node **new_node);
+extern int udf_rename(struct udf_node *old_parent, struct udf_node *rename_me, char *old_name, struct udf_node *new_parent, struct udf_node *present, char *new_name);
+extern int udf_unlink_node(struct udf_node *udf_node);
+
+extern int udf_read_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *buffer, int prefetch_sectors, int rwflags);
+extern int udf_write_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int rwflags, struct udf_wrcallback *wrcallback);
+extern int udf_read_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, uint32_t prefetch_sectors, int rwflags);
+extern int udf_write_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, int rwflags, struct udf_wrcallback *wrcallback);
+
+/* call back */
+extern int udf_writeout_file_buffer(struct udf_node *udf_node, char *what, int rwflags, struct udf_buf *buf_entry);
+
+/* special cases like VRS */
+extern int udf_write_session_cache_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int flags, struct udf_wrcallback *wrcallback);
+
+/* device/disc opener, closer and read/write operations */
+extern void udf_init(void); /* call me first! */
+extern int udf_mount_disc(char *devname, char *range, uint32_t sector_size, int mnt_flags, struct udf_discinfo **disc);
+extern int udf_dismount_disc(struct udf_discinfo *disc);
+extern int udf_open_disc(char *devname, int discop_flags, struct udf_discinfo **disc);
+extern int udf_close_disc(struct udf_discinfo *disc);
+extern int udf_sync_disc(struct udf_discinfo *disc);
+extern int udf_sync_logvol(struct udf_log_vol *udf_log_vol);
+extern int udf_sync_caches(struct udf_log_vol *udf_log_vol);
+extern int udf_open_logvol(struct udf_log_vol *udf_log_vol);
+extern int udf_close_logvol(struct udf_log_vol *udf_log_vol);
+extern int udf_sync_logvol(struct udf_log_vol *udf_log_vol);
+
+
+/* readers/writers helper functions */
+extern int udf_init_session_caches(struct udf_session *udf_session);
+extern int udf_writeout_udf_node(struct udf_node *udf_node, char *why);
+extern int udf_sync_space_tables(struct udf_log_vol *udf_log_vol); /* read comment on definition */
+
+extern int udf_logvol_vpart_to_partition(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_part_mapping **udf_part_mapping_ptr, struct udf_partition **udf_partition_ptr);
+extern int udf_vpartoff_to_sessionoff(struct udf_log_vol *udf_log_vol, struct udf_part_mapping *udf_part_mapping, struct udf_partition *udf_partition, uint64_t offset, uint64_t *ses_off, uint64_t *trans_valid_len);
+
+extern int udf_read_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length);
+extern int udf_read_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length);
+
+extern int udf_write_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback);
+extern int udf_write_partition_descriptor(struct udf_partition *udf_partition, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback);
+extern int udf_write_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback);
+
+
+
+/* exported text-dump functions */
+extern void udf_dump_volume_name(char *prefix, struct udf_log_vol *udf_log_vol);
+extern void udf_dump_long_ad(char *prefix, struct long_ad *adr);
+extern void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp);
+extern void udf_to_unix_name(char *result, char *id, int len, struct charspec *chsp);
+
+
+/* exported descriptor creators */
+extern int udf_validate_tag_sum(union dscrptr *dscr);
+extern int udf_validate_tag_and_crc_sums(union dscrptr *dscr);
+extern uint64_t udf_calc_tag_malloc_size(union dscrptr *dscr, uint32_t udf_sector_size);
+extern int udf_read_fid_stream(struct udf_node *dir_node, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent);
+extern void udf_resync_fid_stream(uint8_t *buffer, uint32_t *fid_pos, uint32_t max_fid_pos, int *fid_found);
+extern int udf_create_empty_anchor_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint32_t main_vds_loc, uint32_t reserve_vds_loc, uint32_t length, struct anchor_vdp **vdp);
+extern int udf_create_empty_primary_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *volset_id, char *privol_name, int vds_num, int max_vol_seq, struct pri_vol_desc **dscrptr);
+extern int udf_create_empty_partition_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, uint16_t part_num, uint32_t access_type, uint32_t start_loc, uint32_t part_len, uint32_t space_bitmap_size, uint32_t unalloc_space_bitmap, struct part_desc **dscrptr);
+extern int udf_create_empty_unallocated_space_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, struct unalloc_sp_desc **dscrptr);
+extern int udf_create_empty_implementation_use_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, struct impvol_desc **dscrptr);
+extern int udf_create_empty_logical_volume_descriptor(uint32_t sector_size, uint16_t dscr_ver, uint16_t serial, char *logvol_name, uint32_t lb_size, uint32_t integrity_start, uint32_t integrity_length, struct logvol_desc **dscrptr);
+extern int udf_create_empty_space_bitmap(uint32_t sector_size, uint16_t dscr_ver, uint32_t num_lbs, struct space_bitmap_desc **dscrptr);
+extern int udf_create_empty_terminator_descriptor(uint32_t sector_size, uint16_t dscr_ver, struct desc_tag **tag);
+extern int udf_create_empty_fileset_desc(uint32_t sector_size, uint16_t dscr_ver, uint32_t fileset_num, char *logvol_name, char *fileset_name, struct fileset_desc **dscrptr);
+extern void udf_add_physical_to_logvol(struct logvol_desc *logvol, uint16_t vol_seq_num, uint16_t phys_part_num);
+extern void udf_derive_new_logvol_integrity(struct udf_log_vol *udf_log_vol);
+
+/* time related creator functions */
+extern void udf_set_timespec_now(struct timespec *timespec);
+extern void udf_set_timestamp_now(struct timestamp *timestamp);
+
+/* exported processing functions */
+extern int udf_proc_pri_vol(struct udf_session *udf_session, struct udf_pri_vol **current, struct pri_vol_desc *incomming);
+extern int udf_proc_part(struct udf_pri_vol *primary, struct udf_partition **current, struct part_desc *incomming);
+extern int udf_proc_log_vol(struct udf_pri_vol *primary, struct udf_log_vol ** current, struct logvol_desc *incomming);
+extern int udf_proc_filesetdesc(struct udf_log_vol *udf_log_vol, struct fileset_desc *incomming);
+extern int udf_sync_space_bitmap(struct udf_alloc_entries *queue, struct space_bitmap_desc *sbd, uint32_t lb_size);
+
+/* exported builders */
+extern int udf_init_udf_node(struct udf_mountpoint *mountpoint, struct udf_log_vol *udf_log_vol, char *what, struct udf_node **udf_nodeptr);
+extern void udf_insert_node_in_hash(struct udf_node *udf_node);
+extern int udf_allocate_udf_node_on_disc(struct udf_node *udf_node);
+extern void udf_node_mark_dirty(struct udf_node *udf_node);
+
+extern int udf_allocate_lbs(struct udf_log_vol *udf_log_vol, int content, uint32_t req_lbs, char *what, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs);
+extern int udf_node_allocate_lbs(struct udf_node *udf_node, int req_lbs, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs);
+
+extern int udf_release_lbs(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t lb_num, uint64_t size);
+extern int udf_node_release_extent(struct udf_node *udf_node, uint64_t from, uint64_t to);
+extern int udf_confirm_freespace(struct udf_log_vol *udf_log_vol, int content, uint64_t size);
+
+
+extern int udf_create_directory_entry(struct udf_node *dir_node, char *componentname, int filetype, int filechar, struct udf_node *refering, struct stat *stat, struct udf_node **new_node);
+extern int udf_unlink_node(struct udf_node *udf_node);
+extern uint64_t udf_increment_unique_id(struct udf_log_vol *udf_log_vol);
+
+/* exported (temp) allocentries */
+extern void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size);
+extern int udf_cut_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t offset);
+extern void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size);
+extern int udf_filepart_mark_extent(struct udf_node *udf_node, uint64_t data_offset, uint64_t data_length, int mark);
+extern int udf_splitup_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae);
+extern int udf_mark_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, int mark, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae);
+extern int udf_extent_properties(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t from, uint64_t to, int *res_all_allocated);
+
+/* statics inside udf */
+SLIST_HEAD(discslist, udf_discinfo) udf_discs_list;
+SLIST_HEAD(volumeset_list, udf_volumeset) udf_volumeset_list;
+SLIST_HEAD(mountables_list, udf_mountpoint) udf_mountables;
+
+
+#endif /* _UDF_H_ */
+
diff --git a/udf_allocentries.c b/udf_allocentries.c
new file mode 100644
index 0000000..41e8e73
--- /dev/null
+++ b/udf_allocentries.c
@@ -0,0 +1,347 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_allocentries.c" is part of the UDFclient toolkit.
+ * File $Id: udf_allocentries.c,v 1.12 2011/02/01 20:43:40 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* XXX strip list to bare minimum XXX */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "udf.h"
+#include "udf_bswap.h"
+#include "udf_discop.h"
+#include "uio.h"
+#include <pthread.h>
+
+
+/* for scsilib */
+extern const char *dvname;
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/* #define DEBUG(a) { a; } */
+#define DEBUG(a) if (0) { a; }
+
+
+/* XXX TODO support non lb_size splits ?? TODO XXX */
+
+/******************************************************************************************
+ *
+ * Basic operations on udf_alloc_entries queues
+ *
+ ******************************************************************************************/
+
+
+void udf_merge_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size) {
+ struct udf_allocentry *alloc_entry, *next_alloc;
+ uint64_t this_end, next_start;
+ int merge;
+
+ TAILQ_FOREACH(alloc_entry, queue, next_alloc) {
+ do {
+ merge = 0;
+
+ /* only non busy ; prolly old cruft? */
+ if (alloc_entry->flags == UDF_SPACE_FREED) break;
+
+ next_alloc = TAILQ_NEXT(alloc_entry, next_alloc);
+ if (next_alloc) {
+ if (next_alloc->flags != alloc_entry->flags) break;
+
+ if (alloc_entry->flags == UDF_SPACE_ALLOCATED) {
+ /* merge on virtual/physical lb_num base; they are automatically adjacent on offset */
+ if (next_alloc->vpart_num != alloc_entry->vpart_num) break;
+ this_end = alloc_entry->lb_num * lb_size + alloc_entry->len;
+ next_start = next_alloc->lb_num * lb_size;
+
+ if (this_end != next_start) break;
+ }
+
+ /* Only merge if merge would result in a legal UDF allocation size */
+ if (((uint64_t) alloc_entry->len + (uint64_t) next_alloc->len) > ((uint64_t) 1<<30)-1) break;
+
+ /* merge! */
+ alloc_entry->len = alloc_entry->len + next_alloc->len;
+ TAILQ_REMOVE(queue, next_alloc, next_alloc);
+ free(next_alloc);
+ merge = 1;
+ }
+ } while (merge);
+ } /* foreach */
+}
+
+
+/* Splits up allocated pieces so that there is a break on `offset' */
+int udf_cut_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t offset) {
+ struct udf_allocentry *entry, *new_entry;
+ uint64_t cur_offset;
+ uint64_t total_length, extra_length, max_slot, new_length;
+ uint64_t entry_offset;
+
+ total_length = 0;
+ TAILQ_FOREACH(entry, queue, next_alloc) {
+ total_length += entry->len;
+ }
+
+ /* printf("Cutting up at offset %ld (lb %ld)\n", offset, offset / lb_size); */
+ if (offset < total_length) {
+ /* split */
+ cur_offset = 0;
+ TAILQ_FOREACH(entry, queue, next_alloc) {
+ if ((offset >= cur_offset) && (offset < cur_offset + entry->len)) {
+ /* overlap */
+ entry_offset = offset - cur_offset;
+ entry_offset = (entry_offset / lb_size) * lb_size;
+ assert(entry_offset % lb_size == 0);
+ if (entry_offset == 0) return 0;
+
+ /* clone the current space */
+ new_entry = calloc(1, sizeof(struct udf_allocentry));
+ if (!new_entry) return ENOMEM;
+ memcpy(new_entry, entry, sizeof(struct udf_allocentry));
+
+ /* split! */
+ /* printf("split up lb %d + lb %d due to lb offset = %ld\n", entry->lb_num, entry->len / lb_size, entry_offset); */
+
+ entry->len = entry_offset;
+ new_entry->len -= entry_offset;
+ new_entry->lb_num += entry_offset / lb_size;
+ TAILQ_INSERT_AFTER(queue, entry, new_entry, next_alloc);
+ DEBUG(printf("split up due to lb offset = %d\n", (int) entry_offset));
+ return 0;
+ }
+ cur_offset += entry->len;
+ }
+ printf("Sanity check: i can't be here\n");
+ exit(1);
+ }
+
+ /* no use to do more if we're there */
+ if (offset == total_length) return 0;
+
+ /*
+ * Glue extra piece on the queue (auto-extending)
+ * see if we reached our `goal'; see if we can just extent the last
+ * allocation entry but NEVER more or equal to one block size for that
+ * would alter semantics.
+ */
+ entry = TAILQ_LAST(queue, udf_alloc_entries);
+ if (!TAILQ_EMPTY(queue)) {
+ extra_length = (uint64_t) lb_size*(((uint64_t) entry->len + lb_size -1) / lb_size) - entry->len;
+ extra_length = MIN(extra_length, (offset - total_length));
+ /* keep semantics: only meant for extending upto blocksize */
+ if (extra_length < lb_size) {
+ entry->len += extra_length;
+ total_length += extra_length;
+ }
+ }
+
+ max_slot = ((((uint64_t) 1<<30)-1) / lb_size) * lb_size;
+ while (offset > total_length) {
+ /* grow queue by adding difference as a zero unallocated space */
+ new_length = offset - total_length;
+ new_length = MIN(max_slot, new_length);
+
+ new_entry = calloc(1, sizeof(struct udf_allocentry));
+ if (!new_entry) return ENOMEM;
+
+ new_entry->len = new_length;
+ new_entry->flags = UDF_SPACE_FREE;
+ TAILQ_INSERT_TAIL(queue, new_entry, next_alloc);
+
+ total_length += new_entry->len;
+ } /* while */
+
+ return 0;
+}
+
+
+/* Splits up allocated pieces so that there is a break on `data_offset' and on `data_offset + data_length' */
+int udf_splitup_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae) {
+ struct udf_allocentry *entry, *prev_entry;
+ uint64_t cur_offset, len;
+ int error;
+
+ entry = prev_entry = NULL;
+#if 0
+ printf("Split %ld + %ld : \n", data_offset, data_length);
+ printf("PRE SPLIT block = lb %d + lb %d (%ld bytes)\n", (int) (data_offset / lb_size), (int) (data_length / lb_size), data_length);
+ TAILQ_FOREACH(entry, queue, next_alloc) {
+ printf("\t(lb %08d + lb %08d[+ %d]) flag %d\n", entry->lb_num, entry->len/lb_size, entry->len % lb_size, entry->flags);
+ }
+ printf("END PRE\n");
+#endif
+
+ /* cut the string at the specified places */
+ error = udf_cut_allocentry_queue(queue, lb_size, data_offset);
+ error = udf_cut_allocentry_queue(queue, lb_size, data_offset + data_length);
+
+#if 0
+ printf("POST SPLIT\n");
+ TAILQ_FOREACH(entry, queue, next_alloc) {
+ printf("\t(lb %08d + lb %08d[+ %d]) flag %d\n", entry->lb_num, entry->len/lb_size, entry->len % lb_size, entry->flags);
+ }
+ printf("END POST\n\n");
+#endif
+
+ if ((res_firstae == NULL) && (res_lastae == NULL)) return 0;
+
+ if (res_firstae) *res_firstae = NULL;
+ if (res_lastae) *res_lastae = NULL;
+
+ DEBUG(printf("SEARCH SPLIT block = %"PRIu64" + %"PRIu64"\n", (data_offset / lb_size), (data_length / lb_size)));
+ /* search the element-range this splitting induced */
+ cur_offset = 0;
+ entry = TAILQ_FIRST(queue);
+ while (entry) {
+ len = entry->len;
+
+ DEBUG(printf("\t(%d + %d) flag %d\n", (int) entry->lb_num, (int) entry->len, (int) entry->flags));
+ if (cur_offset + len > data_offset) {
+ if (res_firstae) *res_firstae = entry;
+ DEBUG(printf("\t\tReturned as first\n"));
+ break;
+ }
+ /* advance */
+ cur_offset += len;
+ entry = TAILQ_NEXT(entry, next_alloc);
+ }
+ prev_entry = entry;
+ while (entry) {
+ len = entry->len;
+
+ if (cur_offset + len > data_offset + data_length) {
+ break;
+ }
+ DEBUG(printf("\t(%d + %d) flag %d\n", (int) entry->lb_num, (int) entry->len, (int) entry->flags));
+
+ /* advance */
+ cur_offset += len;
+ prev_entry = entry;
+ entry = TAILQ_NEXT(entry, next_alloc);
+ }
+ if (res_lastae) *res_lastae = prev_entry;
+ DEBUG(printf("\t\tReturned as last\n\t<skip>\n"));
+
+ DEBUG(printf("END POST\n\n"));
+
+ if (res_firstae) assert(*res_firstae);
+ if (res_lastae) assert(*res_lastae);
+
+ return 0;
+}
+
+
+/* mark a piece with the specified `mark' with no side-effects */
+/* tested OK */
+int udf_mark_allocentry_queue(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t data_offset, uint64_t data_length, int mark, struct udf_allocentry **res_firstae, struct udf_allocentry **res_lastae) {
+ struct udf_allocentry *alloc_entry, *first_alloc_entry, *last_alloc_entry;
+ int error;
+
+ DEBUG(printf("mark %d\n", mark));
+ /* first split up so we don't have to worry about boundaries */
+ error = udf_splitup_allocentry_queue(queue, lb_size, data_offset, data_length, &first_alloc_entry, &last_alloc_entry);
+ assert(error == 0);
+
+ alloc_entry = first_alloc_entry;
+ /* inclusive last_alloc_entry */
+ last_alloc_entry = TAILQ_NEXT(last_alloc_entry, next_alloc);
+ while (alloc_entry != last_alloc_entry) {
+ DEBUG(printf("marking %d + %d into type %d\n", alloc_entry->lb_num, alloc_entry->len/lb_size, mark);)
+ alloc_entry->flags = mark;
+ alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc);
+ }
+
+ if (res_firstae) *res_firstae = first_alloc_entry;
+ if (res_lastae) *res_lastae = last_alloc_entry;
+
+ return 0;
+}
+
+
+int udf_extent_properties(struct udf_alloc_entries *queue, uint32_t lb_size, uint64_t from, uint64_t to, int *res_all_allocated) {
+ struct udf_allocentry *alloc_entry, *first_alloc_entry, *last_alloc_entry;
+ int all_allocated, error;
+
+ /* first split up so we don't have to worry about boundaries */
+ error = udf_splitup_allocentry_queue(queue, lb_size, from, to-from, &first_alloc_entry, &last_alloc_entry);
+ assert(error == 0);
+
+ /* inclusive last_alloc_entry */
+ alloc_entry = first_alloc_entry;
+ last_alloc_entry = TAILQ_NEXT(last_alloc_entry, next_alloc);
+ all_allocated = 1;
+ while (alloc_entry != last_alloc_entry) {
+ all_allocated = all_allocated && ((alloc_entry->flags == UDF_SPACE_ALLOCATED) || (alloc_entry->flags == UDF_SPACE_ALLOCATED_BUT_NOT_USED));
+ alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc);
+ }
+ if (res_all_allocated) *res_all_allocated = all_allocated;
+
+ return 0;
+}
+
+
+void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size) {
+ struct udf_allocentry *entry;
+ uint64_t offset;
+
+ printf("\n%s :", msg);
+ offset = 0;
+ TAILQ_FOREACH(entry, queue, next_alloc) {
+ printf(" [%d : lb %08d till lb %08d] mapped on (lb %d + %d bytes) ",
+ entry->flags, (uint32_t) (offset/lb_size), (uint32_t) (offset + entry->len)/lb_size-1,
+ (uint32_t) (entry->lb_num/lb_size), (uint32_t) entry->len);
+ offset += entry->len;
+ }
+ printf("\n");
+}
+
+
+/* end of udf_allocentries.c */
+
diff --git a/udf_bmap.c b/udf_bmap.c
new file mode 100644
index 0000000..2f182e1
--- /dev/null
+++ b/udf_bmap.c
@@ -0,0 +1,409 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_bmap.c" is part of the UDFclient toolkit.
+ * File $Id: udf_bmap.c,v 1.27 2015/05/04 21:14:54 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* XXX strip list to bare minimum XXX */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "udf.h"
+#include "udf_bswap.h"
+#include "udf_discop.h"
+#include "uio.h"
+#include <pthread.h>
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+//#define DEBUG(a) { a; }
+#define DEBUG(a) if (0) { a; }
+
+
+void udf_dump_allocentry_queue(char *msg, struct udf_alloc_entries *queue, uint32_t lb_size);
+
+
+
+/******************************************************************************************
+ *
+ * Get and release free logical blocks from the free space tables
+ *
+ ******************************************************************************************/
+
+/*
+ * simple metadata distribution algorithm; first part of each block is
+ * metadata followed by data; the offset this blocking can be tweaked by
+ * part_offset.
+ */
+static int udf_allocate_lbs_on_rewritables(struct udf_partition *udf_partition, uint64_t part_offset, uint64_t metadata_granularity, uint32_t lb_size, uint64_t req_size, int content, uint32_t *res_start_lb, uint32_t *res_num_lbs) {
+ struct udf_allocentry *alloc_entry, *chosen;
+ struct udf_alloc_entries *queue;
+ uint64_t start;
+ uint64_t chosen_offset;
+ uint64_t size;
+ int error, is_meta;
+
+ /* allways unalloc_space_queue ? */
+ queue = &udf_partition->unalloc_space_queue;
+
+ /* TODO this is a simplification; no multiple queues yet XXX */
+ is_meta = (content != UDF_C_USERDATA);
+
+ /* start from the beginning of the unallocated space queue */
+ alloc_entry = TAILQ_FIRST(queue);
+ start = alloc_entry->lb_num;
+ assert(start == 0);
+
+ chosen = NULL;
+ chosen_offset = 0;
+
+ size = 0;
+ TAILQ_FOREACH(alloc_entry, queue, next_alloc) {
+ /* we can only start on lb_size (extra sanity check) */
+ assert(start % lb_size == 0);
+
+ /* piecewise if nessisary */
+ size = MIN(alloc_entry->len, req_size);
+ size = (uint64_t) lb_size * (size / lb_size);
+ if (size == 0) continue;
+
+ /* we can only give out on lb_size boundaries */
+ assert(size % lb_size == 0);
+ if (alloc_entry->flags == UDF_SPACE_FREE) {
+ DEBUG(printf("got free entry from %"PRIu64" + %"PRIu64"\n", start / lb_size, (start + alloc_entry->len)/lb_size));
+ if (is_meta) {
+ if (start + req_size + part_offset <= metadata_granularity) {
+ chosen = alloc_entry;
+ chosen_offset = 0;
+ }
+ } else {
+ chosen_offset = 0;
+ if (start + part_offset < metadata_granularity) {
+ chosen_offset = metadata_granularity - (start + part_offset);
+ }
+ if (chosen_offset + size <= alloc_entry->len) {
+ chosen = alloc_entry;
+ }
+ }
+ DEBUG(if (chosen && (size > 0)) printf("chosen\n"));
+ }
+ if (size <= 0) chosen = NULL;
+ if (chosen) break; /* foreach */
+
+ start = start + alloc_entry->len;
+ } /* FOREACH */
+ if (!chosen) return ENOSPC;
+
+ assert(size > 0);
+ assert(size % lb_size == 0);
+ assert(chosen->len % lb_size == 0);
+ assert(start + chosen_offset + size <= start + chosen->len);
+
+ start += chosen_offset;
+ *res_start_lb = start / lb_size;
+ *res_num_lbs = size / lb_size;
+
+ error = udf_mark_allocentry_queue(queue, lb_size, start, size, UDF_SPACE_ALLOCATED, NULL, NULL);
+
+ return error;
+}
+
+
+/* get at most req_lbs from the partition and mark the area used */
+static int udf_allocate_lbs_on_partition(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t req_lbs, int content, uint32_t *res_start_lb, uint32_t *res_num_lbs) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ struct udf_alloc_entries *queue;
+ uint64_t part_start, part_length;
+ uint64_t metadata_granularity, metadata_blk_len;
+ uint64_t part_offset;
+ uint32_t lb_size, num_lbs;
+ uint64_t req_size;
+ uint32_t cnt;
+ int error;
+
+ assert(udf_log_vol);
+ lb_size = udf_log_vol->lb_size;
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition);
+ if (error) return error;
+
+ part_start = (uint64_t) lb_size * udf_rw32(udf_partition->partition->start_loc);
+ part_length = (uint64_t) lb_size * udf_rw32(udf_partition->partition->part_len);
+
+ req_size = (uint64_t) lb_size * req_lbs;
+
+ /* lock against corruption with free */
+ UDF_MUTEX_LOCK(&udf_partition->partition_space_mutex);
+ switch (udf_part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_PHYSICAL :
+ case UDF_PART_MAPPING_SPARABLE :
+ /* sparable is just like the physical mapping */
+
+ /* allways unalloc_space_queue ? */
+ queue = &udf_partition->unalloc_space_queue;
+ udf_merge_allocentry_queue(queue, lb_size);
+ DEBUG(udf_dump_allocentry_queue("Alloc ", queue, lb_size));
+
+ metadata_granularity = part_length / 1024;
+ metadata_blk_len = metadata_granularity / 16;
+
+ part_offset = 0;
+ for (cnt = 0; cnt <= (metadata_granularity / metadata_blk_len); cnt++) {
+ num_lbs = 0;
+ error = udf_allocate_lbs_on_rewritables(udf_partition, part_offset, metadata_granularity, lb_size, req_size, content, res_start_lb, &num_lbs);
+ if (error) {
+ assert(error == ENOSPC);
+ /* disc is most likely getting full with (meta) data; shift window */
+ part_offset += metadata_blk_len;
+ num_lbs = 0;
+ } else {
+ assert(num_lbs >= 1);
+ udf_partition->free_unalloc_space -= num_lbs * lb_size;
+ udf_log_vol->free_space -= num_lbs * lb_size;
+ break; /* for */
+ }
+ }
+ *res_num_lbs = num_lbs;
+ break;
+ case UDF_PART_MAPPING_VIRTUAL :
+ /* strict increasing virtual adress giveout */
+ printf("UDF: get lbs from virtual partition mapping not implemented yet\n");
+ return EBADF;
+ case UDF_PART_MAPPING_META :
+ /* select blobs from the metadata file */
+ if (udf_part_mapping->meta_bitmap_file == NULL)
+ return EROFS;
+ printf("UDF: get lbs from metadata partition mapping not implemented yet\n");
+ break;
+ case UDF_PART_MAPPING_PSEUDO_RW :
+ /* strict increasing adress giveout from the pseudo over. track */
+ printf("UDF: get lbs from pseudo overwritable partition not implemented yet\n");
+ break;
+ }
+
+ /* sanity */
+ if (*res_num_lbs == 0)
+ *res_start_lb = 0;
+
+ /* release partition unalloced and freed space again */
+ UDF_MUTEX_UNLOCK(&udf_partition->partition_space_mutex);
+ return error;
+}
+
+
+/******************************************************************************************
+ *
+ * Upper level free space allocation and releasing.
+ *
+ ******************************************************************************************/
+
+int udf_allocate_lbs(struct udf_log_vol *udf_log_vol, int content, uint32_t req_lbs, char *what, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ uint32_t lb_size, num_lbs;
+ uint16_t vpart_num;
+ int is_meta, ok;
+ int error;
+
+ assert(udf_log_vol);
+ num_lbs = 0; /* shutup gcc */
+ lb_size = udf_log_vol->lb_size;
+
+ /* select udf partition to write to depending on the contents */
+ is_meta = ((content == UDF_C_FIDS) || (content == UDF_C_NODE));
+ vpart_num = is_meta ? udf_log_vol->metadata_vpart : udf_log_vol->data_vpart;
+
+ /* TODO what about when space is freed in an earlier partition ? reset the vpart's ? */
+ DEBUG(printf("UDF allocate %d lbs for node `%s`\n", req_lbs, what));
+ do {
+ DEBUG(printf("do: vpart_num = %d\n", vpart_num));
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition);
+ ok = (udf_partition != NULL) && (udf_part_mapping != NULL);
+ if (ok && !is_meta) ok = udf_part_mapping->data_writable;
+ if (ok && is_meta) ok = udf_part_mapping->metadata_writable;
+ if (ok) {
+ /* try to snoop from this vpart */
+ error = udf_allocate_lbs_on_partition(udf_log_vol, vpart_num, req_lbs, content, res_start_lb, &num_lbs);
+ if (!error) {
+ *res_vpart_num = vpart_num;
+ if (res_num_lbs) *res_num_lbs = num_lbs;
+ }
+ ok = !error;
+ }
+ if (!ok) {
+ DEBUG(printf("advance vpart\n"));
+ vpart_num = is_meta ? ++(udf_log_vol->metadata_vpart) : ++(udf_log_vol->data_vpart);
+ if (vpart_num >= udf_log_vol->num_part_mappings) {
+ udf_log_vol->metadata_vpart = udf_log_vol->data_vpart = 0;
+ printf("UDF: logvol discs full ?\n");
+ return ENOSPC;
+ }
+ }
+ } while (!ok);
+ DEBUG(printf("udf_allocate_lbs: got space on vpart_num = %d; req %d, got %d\n", vpart_num, req_lbs, num_lbs));
+
+ assert((*res_start_lb != 0) && (num_lbs != 0));
+ return 0;
+}
+
+
+int udf_node_allocate_lbs(struct udf_node *udf_node, int req_lbs, uint16_t *res_vpart_num, uint32_t *res_start_lb, uint32_t *res_num_lbs) {
+ char *what;
+ int content;
+
+ /* content can be USERDATA or FID stream here; checking on udf filetype */
+ switch (udf_node->udf_filetype) {
+ case UDF_ICB_FILETYPE_DIRECTORY :
+ case UDF_ICB_FILETYPE_STREAMDIR :
+ content = UDF_C_FIDS;
+ what = "FID stream";
+ break;
+ default :
+ content = UDF_C_USERDATA;
+ what = "file content";
+ break;
+ }
+
+ return udf_allocate_lbs(udf_node->udf_log_vol, content, req_lbs, what, res_vpart_num, res_start_lb, res_num_lbs);
+}
+
+
+/* returns non zero if space is available */
+int udf_confirm_freespace(struct udf_log_vol *udf_log_vol, int content, uint64_t size) {
+ content = content; /* not used now */
+
+ /* generic check for now */
+ if (udf_log_vol->free_space >= udf_log_vol->await_alloc_space + size + UDF_MINFREE_LOGVOL) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int udf_release_lbs(struct udf_log_vol *udf_log_vol, uint16_t vpart_num, uint32_t lb_num, uint64_t size) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ struct udf_alloc_entries *queue;
+ uint32_t lb_size;
+ int error;
+
+ if (!udf_log_vol) return 0;
+
+ lb_size = udf_log_vol->lb_size;
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition);
+ if (error) return error;
+
+ /* space can only be freed in lb_size chuncks by definition */
+ size = (uint64_t) lb_size * ((size + lb_size -1) / lb_size);
+ switch (udf_part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_PHYSICAL :
+ case UDF_PART_MAPPING_SPARABLE :
+ /* sparable is just like the physical mapping */
+ queue = &udf_partition->unalloc_space_queue; /* TODO freed- for non sequential MO media */
+ UDF_MUTEX_LOCK(&udf_partition->partition_space_mutex);
+ error = udf_mark_allocentry_queue(queue, lb_size, (uint64_t) lb_num * lb_size, size, UDF_SPACE_FREE, NULL, NULL);
+ udf_partition->free_unalloc_space += size;
+ udf_log_vol->free_space += size;
+ UDF_MUTEX_UNLOCK(&udf_partition->partition_space_mutex);
+ return error;
+ case UDF_PART_MAPPING_VIRTUAL :
+ /* freeing is not applicable */
+ return 0;
+ case UDF_PART_MAPPING_META :
+ /* free blobs in the metadata file */
+ printf("UDF: freeing lbs from metadata partition mapping not implemented yet\n");
+ break;
+ case UDF_PART_MAPPING_PSEUDO_RW :
+ /* do we have to keep a space bitmap? */
+ printf("UDF: freeing lbs from pseudo rewritable partition mapping not implemented yet\n");
+ break;
+ }
+ return 0;
+
+}
+
+
+/* !!! needs to be called with alloc entry mutex held !!! */
+int udf_node_release_extent(struct udf_node *udf_node, uint64_t from, uint64_t to) {
+ struct udf_allocentry *from_ae, *to_ae, *alloc_entry, *last_alloc_entry;
+ uint32_t lb_size, lbnum, len;
+ uint16_t vpart;
+ int error, flags;
+
+ assert(udf_node->alloc_mutex.locked);
+ assert(udf_node->udf_log_vol);
+ lb_size = udf_node->udf_log_vol->lb_size;
+ error = udf_splitup_allocentry_queue(&udf_node->alloc_entries, lb_size, from, to-from, &from_ae, &to_ae);
+ if (!error) {
+ alloc_entry = from_ae;
+ last_alloc_entry = TAILQ_NEXT(to_ae, next_alloc);
+ while (alloc_entry != last_alloc_entry) {
+ vpart = alloc_entry->vpart_num;
+ lbnum = alloc_entry->lb_num;
+ flags = alloc_entry->flags;
+ len = alloc_entry->len;
+
+ if (flags == UDF_SPACE_ALLOCATED) {
+ error = udf_release_lbs(udf_node->udf_log_vol, vpart, lbnum, len);
+ assert(!error);
+ alloc_entry->flags = UDF_SPACE_FREE; /* WORM: or freed? */
+ } else {
+ DEBUG(printf("udf_filepart_free_extent :freeing a non allocated piece : flags = %d\n", flags));
+ }
+ alloc_entry = TAILQ_NEXT(alloc_entry, next_alloc);
+ }
+ } else {
+ fprintf(stderr, "udf_filepart_free_extent: splitup failed\n");
+ }
+ return 0;
+}
+
+
+/* end of udf_bmap.c */
+
diff --git a/udf_bswap.h b/udf_bswap.h
new file mode 100644
index 0000000..3faf006
--- /dev/null
+++ b/udf_bswap.h
@@ -0,0 +1,161 @@
+/* $NetBSD: udf_bswap.h,v 1.12 2003/04/16 14:27:03 yamt Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * `ported' for UDF by Reinoud Zandijk <reinoud@netbsd.org>
+ *
+ */
+
+#ifndef _UDF_BSWAP_H_
+#define _UDF_BSWAP_H_
+
+#if HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#if HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#if HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+
+
+/* rest only relevant for big endian machines */
+#if (BYTE_ORDER == BIG_ENDIAN)
+//#warning BIG ENDIAN
+//#if (BYTE_ORDER == LITTLE_ENDIAN) /* ENDIAN SWAP... only for testing!!!!! */
+
+/* inlines for access to swapped data */
+static __inline uint16_t udf_rw16 __P((uint16_t));
+static __inline uint32_t udf_rw32 __P((uint32_t));
+static __inline uint64_t udf_rw64 __P((uint64_t));
+
+
+#ifdef HAVE_SYS_BSWAP_H
+#include <sys/bswap.h>
+#endif
+
+#ifdef HAVE_MACHINE_BSWAP_H
+#include <machine/bswap.h>
+#endif
+
+#if (defined(HAVE_SYS_BSWAP_H) || defined(HAVE_MACHINE_BSWAP_H))
+
+/* bswap macro's defined */
+// #warning BSWAP's defined
+
+static __inline uint16_t
+udf_rw16(a)
+ uint16_t a;
+{
+ return bswap16(a);
+}
+
+
+static __inline uint32_t
+udf_rw32(a)
+ uint32_t a;
+{
+ return bswap32(a);
+}
+
+
+static __inline uint64_t
+udf_rw64(a)
+ uint64_t a;
+{
+ return bswap64(a);
+}
+
+#else /* no bswap macro's */
+
+// #warning _NO_ BSWAP's defined
+
+union _bswap_data {
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ uint8_t b[8];
+};
+
+static __inline uint16_t
+udf_rw16(uint16_t a)
+{
+ union _bswap_data b;
+ b.u16 = a;
+ return b.b[0] | (b.b[1] << 8);
+}
+
+
+static __inline uint32_t
+udf_rw32(uint32_t a)
+{
+ union _bswap_data b;
+ b.u32 = a;
+ return b.b[0] | (b.b[1] << 8) | (b.b[2] << 16) | (b.b[3] << 24);
+}
+
+
+static __inline uint64_t
+udf_rw64(uint64_t a)
+{
+ union _bswap_data b;
+ uint32_t low, high;
+ b.u64 = a;
+
+ high = b.b[4] | (b.b[5] << 8) | (b.b[6] << 16) | (b.b[7] << 24);
+ low = b.b[0] | (b.b[1] << 8) | (b.b[2] << 16) | (b.b[3] << 24);
+ return ((uint64_t) low) | ((uint64_t) high) << 32;
+}
+
+#endif /* bswap macro's */
+
+#else /* little endian */
+
+#define udf_rw16(a) ((uint16_t)(a))
+#define udf_rw32(a) ((uint32_t)(a))
+#define udf_rw64(a) ((uint64_t)(a))
+
+#endif
+
+
+#define udf_add16(a, b) \
+ (a) = udf_rw16(udf_rw16((a)) + (b))
+#define udf_add32(a, b) \
+ (a) = udf_rw32(udf_rw32((a)) + (b))
+#define udf_add64(a, b) \
+ (a) = udf_rw64(udf_rw64((a)) + (b))
+
+
+#endif /* !_UDF_BSWAP_H_ */
+
diff --git a/udf_discop.c b/udf_discop.c
new file mode 100644
index 0000000..248a0c1
--- /dev/null
+++ b/udf_discop.c
@@ -0,0 +1,1422 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_discinfo.c" is part of the UDFclient toolkit.
+ * File $Id: udf_discop.c,v 1.79 2015/08/05 18:26:31 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* XXX strip this XXX */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "uscsilib.h"
+#include "udf_discop.h"
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/* #define DEBUG(a) { a; } */
+#define DEBUG(a) if (0) { a; }
+
+
+/* globals */
+
+
+/******************************************************************************************
+ *
+ * Tables and helper functions section
+ *
+ ******************************************************************************************/
+
+int read_cd_hex2(int val) {
+ int nl, nh;
+
+ nl = val & 15;
+ nh = val >> 4;
+ if (nl >= 'A') nl -= 'A' + 10;
+ if (nh >= 'A') nh -= 'A' + 10;
+
+ return (nh*16) + nl;
+}
+
+
+int read_cd_bcd(int val) {
+ int nl, nh;
+
+ nl = (val & 15) - '0';
+ nh = (val >> 4) - '0';
+ if ((nl < 0 || nl > 9) || (nh < 0 || nh > 9)) return val;
+
+ return nh*10 + nl;
+}
+
+
+int32_t cd_msf2lba(int h, int m, int s, int f) {
+ return 270000*h + 4500*m + 75*s + f - 150;
+}
+
+
+/******************************************************************************************
+ *
+ * Disc level operations
+ *
+ ******************************************************************************************/
+
+int udf_discinfo_is_cd_or_dvd(struct udf_discinfo *disc) {
+ /* check device type */
+ switch (disc->devdrv_class & UDF_DEVDRV_CLASS) {
+ case UDF_DEVDRV_CLASS_FILE :
+ case UDF_DEVDRV_CLASS_DISC :
+ /* not nessisary */
+ return 0;
+ case UDF_DEVDRV_CLASS_CD :
+ case UDF_DEVDRV_CLASS_DVD :
+ /* it really is */
+ return 1;
+ default :
+ break;
+ }
+ return ENODEV;
+}
+
+
+int udf_discinfo_check_disc_ready(struct udf_discinfo *disc) {
+ scsicmd cmd;
+ uint8_t buf[36];
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(disc)) return 1;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0; /* test unit ready */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 6, buf, 0, 30000, NULL);
+
+ return (error == 0);
+}
+
+
+#define blk_len 10000
+int udf_discinfo_set_recording_parameters(struct udf_discinfo *discinfo, int testwriting) {
+ scsicmd cmd;
+ uint8_t res[blk_len];
+ uint8_t *pos;
+ uint32_t blockingnr;
+ int val_len, packet;
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return 0;
+
+ /* Set up CD/DVD recording parameters */
+ if (!discinfo->recordable) return 0;
+
+ blockingnr = discinfo->blockingnr;
+
+ DEBUG(printf("Setting device's recording parameters\n"));
+ packet = discinfo->packet;
+
+ val_len = 0x32+2+8;
+ bzero(res, val_len);
+
+ pos = res + 8;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ if (!packet) {
+ pos[ 0] = 0x05; /* page code 5 : cd writing */
+ pos[ 1] = 0x32; /* length in bytes */
+ pos[ 2] = 64 + 0; /* BUFE + write type 1 : track at once */
+ if (testwriting) pos[ 2] += 16;
+ pos[ 3] = (3<<6) | 5; /* next session OK, data packet, rec. incr. var packets */
+ pos[ 4] = 8; /* ISO mode 1 */
+ pos[ 8] = 0; /* normal CD-DA/CD-ROM or data disc */
+ DEBUG(printf("\tsetting up for sequential writing\n"));
+ } else {
+ pos[ 0] = 0x05; /* page code 5 : cd writing */
+ pos[ 1] = 0x32; /* length in bytes */
+ pos[ 2] = 0; /* write type 0 : packet/incremental */
+ if (testwriting) pos[ 2] += 16;
+ pos[ 3] = (3<<6) | 32 | 5; /* next session OK, data packet, rec. incr. fixed packets */
+ pos[ 4] = 10; /* ISO mode 2; XA form 1 */
+ pos[ 8] = 0x20; /* CD-ROM XA disc or DDCD disc */
+ pos[10] = (blockingnr >> 24) & 0xff; /* MSB packet size */
+ pos[11] = (blockingnr >> 16) & 0xff;
+ pos[12] = (blockingnr >> 8) & 0xff;
+ pos[13] = (blockingnr ) & 0xff; /* LSB packet size in SECTORS */
+ DEBUG(printf("\tsetting up for packet writing with packet size %d\n", blockingnr));
+ }
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x55; /* MODE SELECT (10) */
+ cmd[1] = 16; /* PF format */
+ cmd[7] = val_len >> 8; /* length of blob */
+ cmd[8] = val_len & 0xff;
+ cmd[9] = 0; /* control */
+
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, res, val_len, 3000, NULL);
+ if (error) {
+ perror("While WRTITING parameter page 5");
+ return error;
+ }
+
+#if 0
+ /* Set CD/DVD speed to 'optimal' for it doesnt seem to do it automatically */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0xBB; /* Set CD speed */
+ cmd[ 1] = 1; /* select CAV (1) or CLV (0) recording */
+ cmd[ 2] = 0xff;
+ cmd[ 3] = 0xff; /* max read performance speed */
+ cmd[ 4] = 0xff;
+ cmd[ 5] = 0xff; /* max write performance speed; applic? */
+ cmd[11] = 0; /* control */
+ error = scsi_call(SCSI_WRITECMD, discinfo, cmd, 12, NULL, 0, NULL);
+ if (error) {
+ /* CAV not possible? then go for CLV */
+ cmd[ 1] = 0; /* select CAV (1) or CLV (0) recording */
+ error = scsi_call(SCSI_WRITECMD, discinfo, cmd, 12, NULL, 0, NULL);
+ if (error) {
+ perror("While setting speed");
+ return error;
+ }
+ }
+#endif
+
+ /* flag OK */
+ return 0;
+}
+#undef blk_len
+
+
+int udf_discinfo_synchronise_caches(struct udf_discinfo *discinfo) {
+ scsicmd cmd;
+ int error;
+
+ /* bail out when we're in sequential emulation */
+ if (!udf_discinfo_is_cd_or_dvd(discinfo))
+ return 0;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x35; /* Synchronise cache */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL);
+ if (error) {
+ perror("While synchronising write cache");
+ }
+
+ return error;
+}
+
+
+/*
+ * important: it must be called after write operations before read operations
+ * are allowed again. When its allready finished with writing this call has no
+ * effect and can be called at start to make sure the device knows that we're
+ * going to read.
+ */
+int udf_discinfo_finish_writing(struct udf_discinfo *discinfo) {
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo))
+ return 0;
+
+ error = udf_discinfo_synchronise_caches(discinfo);
+ return error;
+}
+
+
+int udf_discinfo_reserve_track_in_logic_units(struct udf_discinfo *discinfo, uint32_t logic_units) {
+ scsicmd cmd;
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x53; /* reserve track */
+ cmd[ 5] = (logic_units >> 24) & 0xff; /* size MSB */
+ cmd[ 6] = (logic_units >> 16) & 0xff;
+ cmd[ 7] = (logic_units >> 8) & 0xff;
+ cmd[ 8] = (logic_units ) & 0xff; /* size LSB */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL);
+
+ return error;
+}
+
+
+int udf_discinfo_close_track(struct udf_discinfo *discinfo, uint16_t trackno) {
+ scsicmd cmd;
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x5B; /* close session/track */
+ cmd[ 2] = 1; /* track */
+ cmd[ 4] = (trackno >> 8) & 0xff; /* specify trackno MSB */
+ cmd[ 5] = (trackno ) & 0xff; /* trackno LSB */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL);
+
+ return error;
+}
+
+
+/* can only close last session */
+int udf_discinfo_close_session(struct udf_discinfo *discinfo) {
+ scsicmd cmd;
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x5B; /* close session/track */
+ cmd[ 2] = 2; /* session */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL);
+
+ return error;
+}
+
+
+/*
+ * Repair a damaged track when suspected. It'll try to make it writable again.
+ * A track can be broken when sudden stops are made and the track end is left
+ * in a inconsistent state in the ATIP/PMA/TOC.
+ */
+int udf_discinfo_repair_track(struct udf_discinfo *discinfo, uint16_t trackno) {
+ scsicmd cmd;
+ int error;
+
+ if (!udf_discinfo_is_cd_or_dvd(discinfo)) return ENODEV;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x58; /* repair track */
+ cmd[ 4] = (trackno >> 8) & 0xff; /* specify trackno MSB */
+ cmd[ 5] = (trackno ) & 0xff; /* trackno LSB */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_WRITECMD, discinfo->dev, cmd, 10, NULL, 0, 30000, NULL);
+
+ return error;
+}
+
+
+/*
+ * This routine 'get disc type' tries to get operational information from the
+ * disc/drive combination and its capabilities. Fills in devdrv_class, MMC
+ * profile and the various flags; no track info.
+ */
+
+int udf_discinfo_get_disc_type(struct udf_discinfo *disc) {
+ struct stat stat;
+ scsicmd cmd;
+ uint8_t buf[256];
+ uint8_t features[1024+100], *rpos, *fpos;
+ uint32_t pos, features_len, feat_tbl_len, val_len;
+ uint32_t feature, last_feature;
+ uint32_t feature_ver, feature_pers, feature_cur, feature_len;
+ int error;
+
+ /* assume generic CD-ROM with no known MMC profile */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_CD;
+ disc->mmc_profile = 0;
+
+ /* check if its a regular file */
+ fstat(disc->dev->fhandle, &stat);
+ if (S_ISREG(stat.st_mode)) {
+ UDF_VERBOSE(printf("UDF device %s is a regular file\n", disc->dev->dev_name));
+ disc->devdrv_class = UDF_DEVDRV_CLASS_FILE;
+ disc->sequential = 0; /* full r/w */
+ disc->recordable = 1; /* assuming rw access */
+ disc->blankable = 0; /* not applicable */
+ disc->rewritable = 1;
+ disc->packet = 0;
+ disc->blockingnr = 1;
+ disc->strict_overwrite = 0;
+ disc->sector_size = DISC_SECTOR_SIZE;
+ return 0;
+ }
+
+ /* check if its a ATIPI/SCSI device */
+ error = uscsi_check_for_scsi(disc->dev);
+ if (error) {
+ /* obviously no ATIPI/SCSI device -> has to be IDE disc or other but no file either */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC;
+ disc->sequential = 0; /* full r/w */
+ disc->recordable = 1; /* assuming rw access */
+ disc->blankable = 0; /* not applicable */
+ disc->rewritable = 1;
+ disc->packet = 0;
+ disc->blockingnr = 1;
+ disc->strict_overwrite = 0;
+ disc->sector_size = DISC_SECTOR_SIZE;
+ UDF_VERBOSE(printf("Got error executing SCSI command, assuming IDE disc\n"));
+ return 0;
+ }
+
+ /* check if its a SCSI disc -> if so, do NOT issue mmc profile check */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x12; /* INQUIRY */
+ cmd[1] = 0; /* basic inquiry */
+ cmd[2] = 0; /* no page or operation code */
+ cmd[3] = 0; /* reserved/MSB result */
+ cmd[4] = 96; /* all but vendor specific */
+ cmd[5] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 6, buf, 96, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "Device claims to be SCSI but does NOT respond to inquiry!\n");
+ return ENOENT;
+ }
+ disc->scsi_device_type = buf[0] & 0x1f;
+
+ switch (disc->scsi_device_type) {
+ case 0x05 : break; /* MMC */
+ case 0x00 : /* Direct access device */
+ case 0x0E : /* Simplified direct access device */
+ /* read-write possible */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC;
+ disc->sequential = 0; /* full r/w */
+ disc->recordable = 1; /* assuming rw access */
+ disc->blankable = 0; /* not applicable */
+ disc->rewritable = 1;
+ disc->packet = 0;
+ disc->blockingnr = 1;
+ disc->strict_overwrite = 0;
+ disc->sector_size = DISC_SECTOR_SIZE;
+ return 0;
+ case 0x04 :
+ case 0x07 :
+ /* Non MMC read only optical media */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC;
+ disc->sequential = 0; /* */
+ disc->recordable = 0; /* assuming ro access */
+ disc->blankable = 0; /* not applicable */
+ disc->rewritable = 0;
+ disc->packet = 0;
+ disc->blockingnr = 1;
+ disc->strict_overwrite = 0;
+ disc->sector_size = DISC_SECTOR_SIZE;
+ return 0;
+ default:
+ fprintf(stderr, "Device type 0x%02x not suitable for mass storage\n", disc->scsi_device_type);
+ return ENOENT;
+ }
+
+ /* get MMC profile */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x46; /* Get configuration */
+ cmd[ 8] = 32; /* just a small buffer size */
+ cmd[ 9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, buf, 32, 30000, NULL);
+ if (!error) {
+ disc->mmc_profile = buf[7] | (buf[6] << 8);
+ } else {
+ disc->mmc_profile = 0; /* mark unknown MMC profile */
+ }
+ UDF_VERBOSE_MAX(printf("Device has MMC profile 0x%02x\n", disc->mmc_profile));
+
+ /* determine CD sector size */
+ bzero(buf, 8);
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, buf, 8, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "Can't read CD recorded capacity; assuming sector size %d : %s", CD_SECTOR_SIZE, strerror(error));
+ disc->sector_size = CD_SECTOR_SIZE;
+ } else {
+ disc->sector_size = buf[7] | (buf[6]<<8) | (buf[5]<<16) | (buf[4]<<24);
+ /* packet size is recorded for each track seperately */
+ }
+
+ /*
+ * Read in features to determine device flags. First take some initial
+ * values.
+ */
+ disc->sequential = 0;
+ disc->recordable = 0;
+ disc->erasable = 0;
+ disc->blankable = 0;
+ disc->formattable = 0;
+ disc->rewritable = 0;
+ disc->mrw = 0;
+ disc->packet = 0;
+ disc->strict_overwrite = 0;
+ disc->blockingnr = 1; /* not relevant if non packet write */
+
+ feat_tbl_len = 1024;
+ last_feature = feature = 0;
+ do {
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x46; /* Get configuration */
+ cmd[1] = 0; /* RT=0 -> all independent of current setting */
+ cmd[2] = (last_feature) >> 8; /* MSB feature number */
+ cmd[3] = (last_feature) & 0xff; /* LSB feature number */
+ cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */
+ cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, features, feat_tbl_len, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "While reading feature table : %s\n", strerror(error));
+ return EIO;
+ }
+
+ features_len = features[3] | (features[2]<<8) | (features[1]<<16) | (features[0]<<24);
+ disc->mmc_profile = features[7] | (features[6]<<8);
+
+ pos = 8;
+ while (pos < features_len) {
+ fpos = &features[pos];
+
+ feature = fpos[1] | (fpos[0] << 8);
+ feature_ver = (fpos[2] >> 2) & 15;
+ feature_cur = (fpos[2] & 1);
+ feature_pers= (fpos[2] & 2);
+ feature_len = fpos[3];
+
+ if (feature_cur == 1) {
+ rpos = &fpos[4];
+ switch (feature) {
+ case 0x0010 : /* random readable feature */
+ disc->sector_size = rpos[3] | (rpos[2] << 8) | (rpos[1] << 16) | (rpos[0] << 24);
+ disc->blockingnr = rpos[5] | (rpos[4] << 8);
+ /* RW error page */
+ break;
+ case 0x0020 : /* random writable feature */
+ disc->recordable = 1;
+ disc->rewritable = 1;
+ break;
+ case 0x0021 : /* incremental streaming write feature */
+ disc->recordable = 1;
+ disc->sequential = 1;
+ disc->link_size = rpos[7];
+ if (rpos[6] & 1)
+ disc->link_size = 0;
+ break;
+ case 0x0022 : /* (obsolete) erase support feature */
+ disc->recordable = 1;
+ disc->erasable = 1;
+ break;
+ case 0x0023 : /* formatting media support feature */
+ disc->recordable = 1;
+ disc->formattable = 1;
+ break;
+ case 0x0024 : /* hardware assisted defect management feature */
+ /* XXX set mrw ? */
+ break;
+ case 0x0025 : /* write once */
+ disc->recordable = 1;
+ break;
+ case 0x0026 : /* restricted overwrite feature */
+ disc->recordable = 1;
+ disc->rewritable = 1;
+ disc->strict_overwrite = 1;
+ break;
+ case 0x0028 : /* MRW formatted media support feature */
+ disc->mrw = 1;
+ break;
+ case 0x002b : /* read/write DVD+R formatted media */
+ disc->sequential = 1;
+ if (rpos[0] & 1) /* write support */
+ disc->recordable = 1;
+ break;
+ case 0x002c : /* regid restricted overwrite feature */
+ disc->recordable = 1;
+ disc->rewritable = 1;
+ disc->strict_overwrite = 1;
+ if (rpos[0] & 1) /* blank bit */
+ disc->blankable = 1;
+ break;
+ case 0x002d : /* track at once recording feature */
+ disc->recordable = 1;
+ disc->sequential = 1;
+ break;
+ case 0x002f : /* DVD-R/-RW write feature */
+ disc->recordable = 1;
+ if (rpos[0] & 2) /* DVD-RW bit */
+ disc->blankable = 1;
+ break;
+ case 0x0038 : /* pseuro overwritable */
+ break;
+ default :
+ /* ignore */
+ break;
+ }
+ }
+
+ last_feature = MAX(last_feature, feature);
+ if (feature_len & 3) {
+ UDF_VERBOSE(printf("Drive returned feature %d %swith bad length %d\n",
+ feature, (feature_cur == 1? "(current) ":""), feature_len));
+ feature_len = (feature_len + 3) & ~3;
+ }
+ pos += 4 + feature_len;
+ }
+ } while (features_len >= 0xffff);
+
+ /*
+ * fixup DVD and CD-RW drives that are on crack.
+ */
+ if (disc->mmc_profile == 0x0a) {
+ /* some forget to mention strict overwrite when not sequential and forget the blocking size */
+ if (!disc->sequential) {
+ disc->strict_overwrite = 1;
+ disc->blockingnr = 32; /* fixed for CD-RW */
+ }
+ /* some say they are strict overwrite but also sequential */
+ if (disc->strict_overwrite)
+ disc->sequential = 0;
+ /* some forget that if they are mrw they can't the be other */
+ if (disc->mrw) {
+ disc->sequential = 0;
+ disc->strict_overwrite = 0;
+ }
+ }
+ /* some think a CD-R is rewritable */
+ if (disc->mmc_profile == 0x09) {
+ disc->rewritable = 0;
+ }
+
+ /* derivatives */
+ if (disc->blockingnr > 1)
+ disc->packet = 1;
+
+ switch (disc->mmc_profile) {
+ case 0x01 :
+ case 0x02 :
+ /* SCSI discs class; treat like normal discs */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_DISC;
+ UDF_VERBOSE(printf("SCSI disc detected; treating like normal disc device\n"));
+ return 0;
+ case 0x03 : /* Magneto Optical with sector erase */
+ case 0x04 : /* Magneto Optical write once */
+ case 0x05 : /* Advance Storage Magneto Optical */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_MO;
+ break;
+ /* 0x00, 0x08, 0x09, 0x0a : different types of CD-ROM devices like CD-R/RW etc. */
+ case 0x00 :
+ disc->devdrv_class = UDF_DEVDRV_CLASS_CD; /* not allways clear */
+ break;
+ case 0x08 : /* CD-ROM */
+ case 0x09 : /* CD-R */
+ case 0x0a : /* CD-RW */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_CD;
+ break;
+ /* 0x10...0x14 DVD-ROM and DVD- devices */
+ case 0x10 : /* DVD-ROM */
+ case 0x11 : /* DVD-R */
+ case 0x12 : /* DVD-RAM */
+ case 0x13 : /* DVD-RW (restricted overwrite) */
+ case 0x14 : /* DVD-RW (sequential) */
+ /* 0x1a..0x1b DVD+ devices */
+ case 0x1a : /* DVD+RW */
+ case 0x1b : /* DVD+R */
+ case 0x2b : /* DVD+R double layer */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_DVD;
+ break;
+ case 0x40 : /* BD-ROM */
+ case 0x41 : /* BD-R Sequential Recording (SRM) */
+ case 0x42 : /* BD-R Random Recording (RRM) */
+ case 0x43 : /* BD-RE */
+ disc->devdrv_class = UDF_DEVDRV_CLASS_BD;
+ break;
+ default :
+ fprintf(stderr, "Not recognized MMC profile %d encountered, marking readonly\n", disc->mmc_profile);
+ disc->devdrv_class = UDF_DEVDRV_CLASS_CD; /* go for the `dummy' */
+ break;
+ }
+
+ /*
+ * ok, if we're on an CD-MRW or DVD+MRW, we ought to have switched
+ * automatically to DMA space. However, this drive at times just
+ * refuses with all messy things around.
+ */
+
+
+ /* Select `Defect managed area' LBA space */
+ if (!disc->mrw)
+ return 0;
+
+ DEBUG(printf("Setting DMA LBA space"));
+
+ val_len = 6 + 8 + 2; /* 2 for 4 byte alignment */
+ rpos = buf + 8;
+ bzero(buf, val_len);
+
+ rpos[ 0] = 0x03; /* GAA/DMA space select */
+ rpos[ 1] = 6; /* page length */
+ rpos[ 3] = 0; /* select GAA bit in bit 0 */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x55; /* MODE SELECT (10) */
+ cmd[1] = 16; /* PF format */
+ cmd[7] = val_len >> 8; /* length of blob */
+ cmd[8] = val_len & 0xff;
+ cmd[9] = 0; /* control */
+
+ error = uscsi_command(SCSI_WRITECMD, disc->dev, cmd, 10, buf, val_len, 30000, NULL);
+ if (error) {
+ perror("While WRTITING parameter page 3");
+ return error;
+ }
+
+ return 0;
+}
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+
+int udf_get_partition_info(struct udf_discinfo *disc) {
+ struct disklabel disklab;
+ struct partition *dp;
+ struct stat st;
+ int partnr;
+
+ /* read disklabel partition */
+ if (ioctl(disc->dev->fhandle, DIOCGDINFO, &disklab) == -1) {
+ /* failed to get disclabel! */
+ perror("disklabel");
+ return errno;
+ }
+
+ /* get disk partition it refers to */
+ fstat(disc->dev->fhandle, &st);
+ partnr = DISKPART(st.st_rdev);
+ dp = &disklab.d_partitions[partnr];
+
+ if (dp->p_size == 0) {
+ perror("faulty disklabel partition returned, check label\n");
+ return EIO;
+ }
+
+ disc->sector_size = disklab.d_secsize;
+ disc->session_start [0] = 0;
+ disc->session_end [0] = dp->p_size - 1;
+
+ return 0;
+}
+
+#elif defined(__linux__)
+#include <linux/fs.h>
+
+int udf_get_partition_info(struct udf_discinfo *disc) {
+ long p_size, p_size512;
+ int secsize;
+
+ /* get device length and sector size */
+ if (ioctl(disc->dev->fhandle, BLKSSZGET, &secsize) == -1) {
+ perror("Can't read my sector size\n");
+ return errno;
+ }
+ if (ioctl(disc->dev->fhandle, BLKGETSIZE, &p_size512) == -1) {
+ perror("Can't read my partition size\n");
+ return errno;
+ }
+
+ p_size = p_size512 * (secsize / 512);
+
+ disc->sector_size = secsize;
+ disc->session_start [0] = 0;
+ disc->session_end [0] = p_size - 1;
+
+ return 0;
+}
+
+#else
+
+int udf_get_partition_info(struct udf_discinfo *disc) {
+ perror(" UDF: no explicit support for disc devices yet for this operating system.\n");
+ perror("Trying readonly access...\n");
+
+ disc->recordable = disc->rewritable = 0;
+
+ return 0;
+}
+
+#endif
+
+
+/* 10000 is arbitrary */
+/* TODO split up one day for its updating values unnessisarily */
+#define res_len 10000
+int udf_get_disc_info(struct udf_discinfo *disc) {
+ scsicmd cmd;
+ struct stat stat;
+ uint32_t val_len;
+ uint32_t first_track, last_track;
+ uint32_t first_session, last_session;
+ uint32_t next_writable_addr, packet_size;
+ uint32_t cntrl, addr, tno, point, min, sec, frame, pmin, psec, pframe;
+ uint32_t data_length, pos;
+ uint8_t res[res_len];
+ int first_track_last_session, last_track_last_session;
+ int track, session, sector_size;
+ int nwa_valid;
+ off_t track_start, track_end, track_size, disc_size, free_blocks;
+ int error;
+
+ if (disc->devdrv_class == UDF_DEVDRV_CLASS_FILE) {
+ sector_size = disc->alt_sector_size ? disc->alt_sector_size : disc->sector_size;
+ fstat(disc->dev->fhandle, &stat);
+ disc->link_size = 0; /* no link lossage */
+ disc->disc_state = DISC_STATE_NOT_SERIAL;
+ disc->last_session_state = SESSION_STATE_INCOMPLETE;
+ disc->num_sessions = 1;
+ disc->session_start [0] = 0;
+ disc->session_end [0] = (stat.st_size / sector_size); /* inclusive */
+ disc->next_writable [0] = (stat.st_size / sector_size) + 1;
+ disc->packet_size [0] = stat.st_blksize / sector_size;
+ return 0;
+ }
+
+ if (disc->devdrv_class == UDF_DEVDRV_CLASS_DISC) {
+ disc->link_size = 0; /* no link lossage */
+ disc->disc_state = DISC_STATE_NOT_SERIAL;
+ disc->last_session_state = SESSION_STATE_COMPLETE;
+ disc->num_sessions = 1;
+
+ disc->session_start [0] = 0;
+ disc->session_end [0] = 0;
+
+ error = udf_get_partition_info(disc);
+ if (error)
+ return error;
+
+ fprintf(stderr, "UDF: warning... reading/writing on 'disc' device\n");
+ return 0;
+ }
+
+ /* classes CD and DVD remain; we can be on a DVD/CD recordable or on a legacy CD-ROM */
+
+ /* Get track information */
+ val_len = 12;
+ bzero(res, val_len);
+ bzero(cmd, SCSI_CMD_LEN);
+
+ cmd[0] = 0x51; /* Read disc information */
+ cmd[7] = val_len >> 8; /* MSB allocation length */
+ cmd[8] = val_len & 0xff; /* LSB allocation length */
+ cmd[9] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL);
+
+ if (!error) {
+ /* we are a MMC3+ device! */
+ disc->disc_state = res[2] & 3; /* just 'happends' to be the same as we use */
+ disc->last_session_state = (res[2] >> 2) & 3; /* ditto */
+ disc->blankable = res[2] & 16; /* blankable -> update possibility */
+ disc->num_sessions = res[4] | (res[ 9] << 8);
+ first_track = res[3];
+ first_track_last_session = res[5] | (res[10] << 8); /* to build up the last 'session' */
+ last_track_last_session = res[6] | (res[11] << 8);
+
+ /* Initialise the sessions to be taken as overspanning tracks */
+ for (session = 0; session < disc->num_sessions; session++) {
+ disc->session_start[session] = INT_MAX;
+ disc->session_end [session] = 0;
+ }
+
+ for (track = first_track; track <= last_track_last_session; track++) {
+ /* each track record is 36 bytes long */
+ val_len = 36;
+ bzero(res, val_len);
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x52; /* Read track information */
+ cmd[1] = 1; /* indexed on track */
+ cmd[4] = track >> 8; /* track number 0-0xff ? */
+ cmd[5] = track & 0xff;
+ cmd[7] = val_len >> 8; /* MSB length resultbuf */
+ cmd[8] = val_len & 0xff; /* LSB ,, */
+ cmd[9] = 0;
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL);
+ if (error) {
+ perror("While reading track info");
+ break;
+ }
+#if 0
+ data_length = res[1] | (res[0] << 8);
+ // track_number = res[2] | (res[32] << 8); /* why? */
+ session = res[3] | (res[33] << 8);
+ // is_track_mode = res[5] & 15;
+ // is_copy = res[5] & 16;
+ // is_damage = res[5] & 32;
+ // is_fixed_packet = res[6] & 16;
+ // is_packet_or_inc = res[6] & 32;
+ // is_blank = res[6] & 64;
+ // is_reserved = res[6] & 128;
+ // is_data_mode = res[6] & 15;
+ nwa_valid = res[7] & 1;
+ // lra_valid = res[7] & 2;
+#endif
+ data_length = res[1] | (res[0] << 8);
+ session = res[3] | (res[33] << 8);
+ nwa_valid = res[7] & 1;
+
+ track_start = res[11] | (res[10]<<8) | (res[ 9]<<16) | (res[ 8]<<24);
+ next_writable_addr = res[15] | (res[14]<<8) | (res[13]<<16) | (res[12]<<24);
+ free_blocks = res[19] | (res[18]<<8) | (res[17]<<16) | (res[16]<<24);
+ packet_size = res[23] | (res[22]<<8) | (res[21]<<16) | (res[20]<<24);
+ track_size = res[27] | (res[26]<<8) | (res[25]<<16) | (res[24]<<24);
+ /* last_recorded_addr = res[32] | (res[30]<<8) | (res[29]<<16) | (res[28]<<24); */
+ track_end = track_start + track_size;
+
+ if (data_length <= 30) session &= 0xff;
+
+ disc->session_start[session-1] = MIN(disc->session_start[session-1], track_start);
+ disc->session_end [session-1] = MAX(disc->session_end [session-1], track_end);
+ disc->free_blocks [session-1] = free_blocks;
+ disc->packet_size [session-1] = packet_size;
+ if (nwa_valid) disc->next_writable[session-1] = next_writable_addr;
+ }
+ if (disc->session_start[disc->num_sessions-1] == INT_MAX) {
+ if (disc->disc_state == DISC_STATE_FULL) {
+ if (disc->last_session_state == SESSION_STATE_COMPLETE) disc->num_sessions--;
+ }
+ }
+
+ /* XXX
+ * initialise default link size to zero; only set DEFAULT
+ * link lossage for CD-R we are using `Burn Free' for writing
+ * but if it fails its good to know the penalty for recovery
+ * XXX
+ */
+ disc->link_size = 0;
+ if (disc->mmc_profile == 0x09) {
+ disc->link_size = 7;
+ }
+
+ return 0;
+ }
+
+ /* Start with legacy CD-ROM .... trying to get as much info from it as possible */
+ disc->sequential = 0;
+ disc->recordable = 0;
+ disc->blankable = 0;
+ disc->packet = 0;
+ disc->blockingnr = 32;
+ disc->strict_overwrite = 0;
+ disc->disc_state = DISC_STATE_FULL;
+ disc->last_session_state = SESSION_STATE_COMPLETE;
+ disc->link_size = 7; /* DEFAULT link lossage for CDs */
+
+
+ /* Read session range */
+ val_len = 4; /* only head */
+ bzero(res, val_len);
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* no LBA's are defined */
+ cmd[2] = 2; /* format 2 also this time */
+ cmd[6] = 0; /* obligatory zero */
+ cmd[7] = val_len >> 8;
+ cmd[8] = val_len & 0xff;
+ cmd[9] = 0;
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL);
+ if (error) {
+ perror("TOC reading of sessions failed");
+ return error;
+ }
+ first_session = read_cd_hex2(res[2]);
+ last_session = read_cd_hex2(res[3]);
+
+ disc->num_sessions = last_session - first_session + 1;
+
+ /* Initialise the sessions to be taken as overspanning tracks */
+ for(session = 0; session <= disc->num_sessions; session++) {
+ disc->session_start[session] = INT_MAX;
+ disc->session_end [session] = 0;
+ }
+
+ /* calculate how big the result buffer ought to be to get the whole TOC */
+ /* NOTE: don't count the 2 length bytes */
+ val_len = res[1] + (res[0] << 8);
+
+ /* fix length for ATAPI drives */
+ if (val_len & 1)
+ val_len++;
+
+ assert(val_len < res_len);
+
+ /* Read the complete TOC and extract information */
+ bzero(res, val_len);
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
+ cmd[1] = 2; /* LBA's are not defined */
+ cmd[2] = 2; /* format 2; full TOC */
+ cmd[6] = first_session; /* start at first session */
+ cmd[7] = val_len >> 8;
+ cmd[8] = val_len & 0xff;
+ cmd[9] = 0;
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, val_len, 30000, NULL);
+ if (error) {
+ perror("TOC reading of full TOC failed");
+ return error;
+ }
+
+ data_length =res[1] | (res[0] << 8);
+ if (data_length != val_len) {
+ fprintf(stderr, "Warning: device didn't return all data requested when reading full TOC\nn");
+ fprintf(stderr, "\treturned %d bytes instead of %d\n", data_length, val_len);
+ }
+ pos = 4;
+ track_start = INT_MAX; track_end = 0;
+
+ while (pos < data_length + 2) {
+ session = read_cd_bcd(res[pos+ 0]);
+ cntrl = res[pos+1] & 15;
+ addr = res[pos+1] >> 4;
+ tno = read_cd_bcd(res[pos+ 2]);
+ point = read_cd_bcd(res[pos+ 3]);
+ min = read_cd_bcd(res[pos+ 4]);
+ sec = read_cd_bcd(res[pos+ 5]);
+ frame = read_cd_bcd(res[pos+ 6]);
+ pmin = read_cd_bcd(res[pos+ 8]);
+ psec = read_cd_bcd(res[pos+ 9]);
+ pframe = read_cd_bcd(res[pos+10]);
+
+ /* extract information; explicit writeout. See SCSI docs */
+ if (tno == 0 && session && addr == 1) {
+ switch (point) {
+ case 0xa0 : first_track = pmin; break;
+ case 0xa1 : last_track = pmin; break;
+ case 0xa2 :
+ track_end = cd_msf2lba(0, pmin, psec, pframe);
+ disc->session_end [session-1] = MAX(disc->session_end [session-1], track_end);
+ break;
+ default :
+ track_start = cd_msf2lba(0, pmin, psec, pframe);
+ disc->session_start[session-1] = MIN(disc->session_start[session-1], track_start);
+ break;
+ }
+ }
+ if (tno == 0 && session && addr == 5) {
+ if (point == 0xb0) {
+ next_writable_addr = cd_msf2lba(0, min, sec, frame);
+ disc_size = cd_msf2lba(0, pmin, psec, pframe);
+ /* TODO use nwa & size */
+ printf("UDF: ignoring B0 Q channel : next writable address; pre MMC3 device; fix me\n");
+ }
+
+ }
+ pos += 11;
+ }
+
+ /* Last session information is notoriously flawed in TOC format so update it */
+ bzero(res, 8);
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, res, 8, 30000, NULL);
+ if (error) {
+ fprintf(stderr, "Can't read CD recorded capacity, last session end might not be OK : %s\n", strerror(error));
+ return 0;
+ }
+
+ session = disc->num_sessions-1;
+ disc->session_end[session] = res[3] | (res[2]<<8) | (res[1]<<16) | (res[0]<<24);
+
+ return 0;
+}
+#undef res_len
+
+
+int udf_open_disc(char *dev_name, int discop_flags, struct udf_discinfo **discptr) {
+ struct udf_discinfo *disc;
+
+ if (!discptr) return EINVAL;
+ *discptr = NULL;
+
+ /* determine what kind of file/device we are dealing with */
+ disc = calloc(1, sizeof(struct udf_discinfo));
+ if (!disc) return ENOMEM;
+
+ disc->dev = calloc(1, sizeof(struct uscsi_dev));
+ if (!disc->dev) {
+ free(disc);
+ return ENOMEM;
+ }
+
+ /* fill in the name */
+ disc->dev->dev_name = strdup(dev_name);
+
+ if (uscsi_open(disc->dev) != 0) {
+ perror("Failure to open device or file");
+ free(disc->dev);
+ free(disc);
+ return ENODEV;
+ }
+
+ /* determine disc type */
+ if (udf_discinfo_get_disc_type(disc)) {
+ perror("Error during classification of disc; skipping disc\n");
+ uscsi_close(disc->dev);
+ free(disc->dev);
+ free(disc);
+ return ENODEV;
+ }
+
+ /* get disc info */
+ if (udf_get_disc_info(disc)) {
+ fprintf(stderr, "Can't get disc info");
+ uscsi_close(disc->dev);
+ free(disc->dev);
+ free(disc);
+ return ENODEV;
+ }
+
+ /* process discop_flags */
+ if (discop_flags & UDF_DISCOP_BSWAP)
+ disc->bswap_sectors = 1;
+
+ /* return the pointer */
+ *discptr = disc;
+
+ /* set recording parameters */
+ udf_discinfo_set_recording_parameters(disc, 0); /* no testwrite */
+
+ return 0;
+}
+
+
+int udf_close_disc(struct udf_discinfo *disc) {
+ if (!disc) return 0;
+
+/* udf_stop_disc_thread(disc); */
+ uscsi_close(disc->dev);
+
+ printf("Disc access statistics\n");
+ printf("\tsector reads %8"PRIu64" (%"PRIu64" Kbyte)\n", disc->sectors_read, ((uint64_t) (disc->sectors_read) * disc->sector_size) / 1024);
+ printf("\tsector written %8"PRIu64" (%"PRIu64" Kbyte)\n", disc->sectors_written, ((uint64_t) (disc->sectors_written) * disc->sector_size) / 1024);
+ printf("\tswitches %8d\n", disc->switchings);
+
+ return 0;
+}
+
+
+
+int udf_discinfo_alter_perception(struct udf_discinfo *disc, uint32_t sec_size, uint32_t num_sectors) {
+ struct stat stat;
+
+ assert(disc);
+ if ((disc->devdrv_class & UDF_DEVDRV_CLASS) != UDF_DEVDRV_CLASS_FILE) {
+ return EINVAL;
+ }
+
+ fstat(disc->dev->fhandle, &stat);
+ if (sec_size == 0) sec_size = disc->sector_size;
+ if (num_sectors == 0) num_sectors = stat.st_size / sec_size;
+
+ if (((sec_size % 512) != 0) || (sec_size == 0)) {
+ fprintf(stderr, "Size of blocks need to be a multiple of 512\n");
+ return EINVAL;
+ }
+
+ if (num_sectors < 300) {
+ fprintf(stderr, "Disc size too small to put an UDF filingsystem on\n");
+ return EINVAL;
+ }
+
+ if (stat.st_size != (off_t) sec_size * num_sectors) {
+ fprintf(stderr, "Size of image file is not equal to specified size parameters\n");
+ return EINVAL;
+ }
+
+ disc->sequential = 0; /* full r/w */
+ disc->recordable = 1; /* assuming rw access */
+ disc->rewritable = 1;
+ disc->sector_size = sec_size;
+ disc->alt_sector_size = sec_size; /* altered value */
+ disc->link_size = 0; /* no link lossage */
+ disc->disc_state = DISC_STATE_NOT_SERIAL;
+ disc->last_session_state = SESSION_STATE_INCOMPLETE;
+ disc->num_sessions = 1;
+ disc->session_start [0] = 0;
+ disc->session_end [0] = num_sectors;
+ disc->next_writable [0] = num_sectors + 1;
+ disc->packet_size [0] = stat.st_blksize / sec_size; /* best blocking size */
+
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * Sector readers and writers
+ *
+ ******************************************************************************************/
+
+
+/* read an extent of sectors in the `result' buffer */
+int udf_read_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *result) {
+ struct uscsi_sense sense;
+ scsicmd cmd;
+ int size, size_read, chunk;
+ uint32_t session, skipped, sector_size;
+ int pos, lb, hb, error;
+
+ /* protect us */
+ if (((long) result) & 3) {
+ printf("Unaligned read of sector : possible panic() on some systems avoided\n");
+ return EIO;
+ }
+
+ sector_size = disc->sector_size;
+
+ /* read one UDF sector at a physical address specified in UDF_SECTOR size units */
+ size = num_sectors * disc->sector_size;
+ size_read = 0;
+ bzero(result, size); /* just in case */ /* has to go? */
+
+ assert(sector_size);
+ assert(num_sectors <= 0xffff);
+
+ /* statistics and cache control */
+ if (disc->am_writing) {
+ disc->switchings++;
+ /* XXX how about pseudo-overwrite? is this nessisary then too ? XXX */
+ if (disc->sequential) {
+ /*
+ * we need to synchronise the write caches before we
+ * are allowed to read from the disc again.
+ */
+ error = udf_discinfo_synchronise_caches(disc);
+ while (error) {
+ printf("udf_discinfo: failed to sync caches, retrying\n");
+ error = udf_discinfo_synchronise_caches(disc);
+ }
+ /* we need to update our NWA's after the sync on sequentials */
+ udf_get_disc_info(disc);
+ }
+ /* mark reading access only */
+ disc->am_writing = 0;
+ }
+
+ error = 0;
+ DEBUG(printf("\r%08d : %s; read %d bytes\n", (int) sector, what, size));
+ while (num_sectors) {
+ switch (disc->devdrv_class & UDF_DEVDRV_CLASS) {
+ case UDF_DEVDRV_CLASS_CD :
+ case UDF_DEVDRV_CLASS_DVD :
+ /*
+ * Use a SCSI command to read it so we can go past the last session;
+ * allthough READ (12) is available, some older CD-ROM
+ * devices only want to do READ (10).
+ */
+
+ /* limited by MAXPHYS, taking 64kb as limitation */
+ chunk = MIN(64*1024 / sector_size, num_sectors);
+ size_read = chunk * sector_size; /* definition */
+ skipped = session = 0;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x28; /* READ (10) command */
+ cmd[1] = 0; /* normal access */
+ cmd[2] = (sector >> 24) & 0xff;
+ cmd[3] = (sector >> 16) & 0xff;
+ cmd[4] = (sector >> 8) & 0xff;
+ cmd[5] = (sector ) & 0xff;
+ cmd[6] = 0; /* reserved */
+ cmd[7] = (chunk >> 8) & 0xff; /* MSB transfer */
+ cmd[8] = (chunk ) & 0xff; /* number of logical block(s) */
+ cmd[9] = 0; /* control */
+ do {
+ error = uscsi_command(SCSI_READCMD, disc->dev, cmd, 10, result, size_read - skipped, 30000, &sense);
+ /* TODO: if busy, ask drive how long it'll take to be available again and wait */
+ if (sense.asc == 4)
+ usleep(5000);
+ } while (sense.asc == 4);
+
+ if (error) return ENOENT;
+ break;
+ default :
+ /* XXX first and only reference to {p}read() XXX */
+ if (sector>=0)
+ size_read = pread(disc->dev->fhandle, result, (uint64_t) num_sectors * sector_size, sector * sector_size);
+ break;
+ }
+ /* statistics */
+ disc->sectors_read += size_read / sector_size;
+
+ /* swap space if requested */
+ if (disc->bswap_sectors) {
+ for (pos = 0; pos < size_read;) {
+ lb = result[pos ];
+ hb = result[pos+1];
+ result[pos ] = hb;
+ result[pos+1] = lb;
+ pos += 2;
+ }
+ }
+
+ /* advance */
+ num_sectors -= size_read / sector_size;
+ sector += size_read / sector_size;
+ result += size_read;
+ if (size_read <= 0) {
+ UDF_VERBOSE_MAX(
+ if (what) {
+ printf("Can't read sectors %d+%d for %s\n", (int) sector, num_sectors, what);
+ }
+ );
+ if (size_read == 0) return ENOENT;
+ return error;
+ }
+ }
+
+ return 0; /* flag ok */
+}
+
+
+/* write an extent of sectors to disc */
+int udf_write_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *source) {
+ struct uscsi_sense sense;
+ scsicmd cmd;
+ int trans_length, size, size_written, chunk;
+ uint32_t sector_size;
+ uint8_t *buffer;
+ int lb, hb, pos, error;
+
+/* if (!disc->udf_recording) return ENODEV; */
+
+ /* protect us */
+ if (((long) source) & 3) {
+ printf("Unaligned write of sector : possible panic() on some systems avoided\n");
+ return EIO;
+ }
+
+ sector_size = disc->sector_size;
+
+ /* XXX clean up XXX */
+ assert(sector_size);
+ assert(num_sectors <= 0xffff); /* compatible with WRITE (10)? */
+
+ DEBUG(printf("\r%08d : %s ;WRITE %d bytes\n", (int) sector, what, num_sectors * sector_size));
+
+ /* swap space if requested */
+ buffer = source;
+ if (disc->bswap_sectors) {
+ size = num_sectors * sector_size;
+ buffer = malloc(num_sectors * sector_size);
+ for (pos = 0; pos < size;) {
+ lb = source[pos ];
+ hb = source[pos+1];
+ buffer[pos ] = hb;
+ buffer[pos+1] = lb;
+ pos += 2;
+ }
+ }
+
+ error = 0;
+ while (num_sectors) {
+ size_written = 0;
+
+ switch (disc->devdrv_class & UDF_DEVDRV_CLASS) {
+ case UDF_DEVDRV_CLASS_CD :
+ case UDF_DEVDRV_CLASS_DVD :
+ /*
+ * Use WRITE (12) command to write to the disc; we
+ * might have to downgrade later to using WRITE (10)
+ * on older discs though :-/
+ */
+
+ /* limited by MAXPHYS, taking 64kb as limitation */
+ chunk = MIN(64*1024 / sector_size, num_sectors); /* in sectors */
+ trans_length = chunk; /* in sectors */
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0xAA; /* WRITE (12) */
+ cmd[ 1] = 0; /* no force unit access */
+ cmd[ 2] = (sector >> 24) & 0xff;
+ cmd[ 3] = (sector >> 16) & 0xff;
+ cmd[ 4] = (sector >> 8) & 0xff;
+ cmd[ 5] = (sector ) & 0xff;
+ cmd[ 6] = (trans_length >> 24) & 0xff; /* MSB */
+ cmd[ 7] = (trans_length >> 16) & 0xff;
+ cmd[ 8] = (trans_length >> 8) & 0xff;
+ cmd[ 9] = (trans_length ) & 0xff; /* LSB */
+ cmd[10] = 0; /* no streaming */
+ cmd[11] = 0; /* control */
+ do {
+ error = uscsi_command(SCSI_WRITECMD, disc->dev, cmd, 12, buffer, trans_length * sector_size, 30000, &sense);
+ /* TODO: if busy, ask drive how long it'll take to be available again and wait */
+ if (sense.asc == 4)
+ usleep(5000);
+ } while (sense.asc == 4);
+
+ /* write one UDF sector at a physical address specified in UDF_SECTOR size units */
+ size = trans_length * sector_size;
+ size_written = error ? 0 : size;
+ break;
+ default :
+ /* XXX first and only reference to {p}write() XXX */
+ DEBUG(printf("udf_discop: pwrite %"PRIu64" + %d\n", (uint64_t) sector * sector_size, (int) num_sectors * sector_size));
+ /* write one UDF sector at a physical address specified in UDF_SECTOR size units */
+ size = num_sectors * sector_size;
+ size_written = pwrite(disc->dev->fhandle, buffer, (uint64_t) num_sectors * sector_size, sector * sector_size);
+ if (size_written < 0)
+ size_written = 0;
+ break;
+ }
+
+ /* free our copy if we created one */
+ if (buffer != source)
+ free(buffer);
+
+ /* statistics */
+ disc->sectors_written += size_written / sector_size;
+ if (!disc->am_writing) disc->switchings++;
+ disc->am_writing = 1;
+
+ num_sectors -= size_written / sector_size;
+ sector += size_written / sector_size;
+ buffer += size_written;
+ if ((size_written < size) || error) {
+ DEBUG(if (error) printf("Writing %s at sectors %d+%d failed\n", what, (int) sector, num_sectors));
+ return EIO;
+ }
+ }
+
+ return 0;
+}
+
+
+/* End of udf_discinfo.c */
+
diff --git a/udf_discop.h b/udf_discop.h
new file mode 100644
index 0000000..3d54977
--- /dev/null
+++ b/udf_discop.h
@@ -0,0 +1,148 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_discop.h" is part of the UDFclient toolkit.
+ * File $Id: udf_discop.h,v 1.30 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef _UDF_DISCOP_H_
+#define _UDF_DISCOP_H_
+
+
+#include <pthread.h>
+#include "defs.h"
+#include "uscsilib.h"
+#include "queue.h"
+
+/* setup constants */
+#define UDF_DISCOP_BSWAP 1
+
+/* Implementation constants */
+#define MAX_SESSIONS 100
+#define CD_SECTOR_SIZE 2048
+#define DISC_SECTOR_SIZE 512
+
+
+/* State and driver constants */
+#define DISC_STATE_EMPTY 0 /* nothing on the disc */
+#define DISC_STATE_INCOMPLETE 1 /* can be appended */
+#define DISC_STATE_FULL 2 /* no appending possible */
+#define DISC_STATE_NOT_SERIAL 3 /* not serial */
+
+#define SESSION_STATE_EMPTY 0 /* still empty */
+#define SESSION_STATE_INCOMPLETE 1 /* session is open */
+#define SESSION_STATE_CLOSED 2 /* we need a new session */
+#define SESSION_STATE_COMPLETE 3 /* no appending possible */
+
+#define UDF_DEVDRV_CLASS 0xFF
+#define UDF_DEVDRV_CLASS_FILE 0x00
+#define UDF_DEVDRV_CLASS_DISC 0x01
+#define UDF_DEVDRV_CLASS_CD 0x02
+#define UDF_DEVDRV_CLASS_DVD 0x04
+#define UDF_DEVDRV_CLASS_MO 0x08
+#define UDF_DEVDRV_CLASS_BD 0x10
+
+
+/* will go? */
+struct udf_session;
+
+
+/*
+ * The complete disc, all cd sessions and how to access the drive
+ */
+struct udf_discinfo {
+ struct uscsi_dev *dev; /* uscsi device associated */
+
+ int scsi_device_type; /* in case of SCSI */
+ int mmc_profile; /* in case of SCSI */
+ int devdrv_class;
+
+ int sequential; /* media is sequential writable only */
+ int recordable; /* media is record-able; i.e. not static */
+ int erasable; /* drive can erase sectors */
+ int blankable; /* media can be blanked by the drive */
+ int formattable; /* media can be formatted by the drive */
+ int rewritable; /* media can be rewritten */
+ int mrw; /* media is identified as being MRW formatted */
+ int packet; /* media is using packet recording */
+ int strict_overwrite; /* media can only be written a packet at a time */
+ int blockingnr; /* blocking size for ECC/modification */
+
+ int sector_size; /* sector size in bytes */
+ int alt_sector_size; /* if non zero, override normal sectorsize */
+ int link_size; /* link size lossage when having underruns */
+ int disc_state; /* empty - incomplete - full - not_serial */
+ int last_session_state; /* empty - incomplete - closed - complete */
+
+ /* flags that alter udf_discop's behaviour */
+ int udf_recording; /* flags if a recording is initialised */
+ int bswap_sectors; /* swap all bytes in a sector */
+
+ /* statistics */
+ int am_writing;
+ uint64_t sectors_read;
+ uint64_t sectors_written;
+ uint32_t switchings;
+
+ /* sessions */
+ int num_sessions;
+ int num_udf_sessions;
+ int session_is_UDF[MAX_SESSIONS];
+ int session_quirks[MAX_SESSIONS];
+
+ off_t session_start [MAX_SESSIONS];
+ off_t session_end [MAX_SESSIONS];
+
+ uint32_t next_writable [MAX_SESSIONS];
+ uint32_t free_blocks [MAX_SESSIONS];
+ uint32_t packet_size [MAX_SESSIONS];
+
+ STAILQ_HEAD(udf_sessions, udf_session) sessions;
+
+ SLIST_ENTRY(udf_discinfo) next_disc;
+};
+
+
+/* exported functions */
+extern int udf_open_disc(char *dev_name, int discop_flags, struct udf_discinfo **discptr);
+extern int udf_close_disc(struct udf_discinfo *disc);
+
+extern int udf_discinfo_get_type(struct udf_discinfo *disc);
+extern int udf_discinfo_check_disc_ready(struct udf_discinfo *disc);
+extern int udf_discinfo_is_cd_or_dvd(struct udf_discinfo *disc);
+extern int udf_discinfo_set_recording_parameters(struct udf_discinfo *discinfo, int testwriting);
+extern int udf_discinfo_alter_perception(struct udf_discinfo *disc, uint32_t sec_size, uint32_t num_sectors);
+extern int udf_discinfo_finish_writing(struct udf_discinfo *discinfo);
+extern int udf_discinfo_reserve_track_in_logic_units(struct udf_discinfo *discinfo, uint32_t logic_units);
+extern int udf_discinfo_close_track(struct udf_discinfo *discinfo, uint16_t trackno);
+extern int udf_discinfo_repair_track(struct udf_discinfo *discinfo, uint16_t trackno);
+
+extern int udf_read_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *result);
+extern int udf_write_physical_sectors(struct udf_discinfo *disc, off_t sector, uint32_t num_sectors, char *what, uint8_t *source);
+
+#endif /* _UDF_DISCOP_H_ */
+
diff --git a/udf_readwrite.c b/udf_readwrite.c
new file mode 100644
index 0000000..83caf29
--- /dev/null
+++ b/udf_readwrite.c
@@ -0,0 +1,842 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_readwrite.c" is part of the UDFclient toolkit.
+ * File $Id: udf_readwrite.c,v 1.49 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* XXX strip list to bare minimum XXX */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "udf.h"
+#include "udf_bswap.h"
+#include "udf_discop.h"
+#include "uio.h"
+#include <pthread.h>
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/* #define DEBUG(a) { a; } */
+#define DEBUG(a) if (0) { a; }
+
+
+/* predefines */
+#if 1
+ extern void udf_dump_descriptor(union dscrptr *dscrpt);
+#else
+ void udf_dump_descriptor(union dscrptr *dscrptr) {}
+#endif
+
+
+int udf_writeout_session_cache(struct udf_session *udf_session);
+
+
+/******************************************************************************************
+ *
+ * Session-cache init and syncing
+ *
+ ******************************************************************************************/
+
+int udf_init_session_caches(struct udf_session *udf_session) {
+ uint32_t sector_size;
+
+ sector_size = udf_session->disc->sector_size;
+
+ UDF_MUTEX_INIT(&udf_session->session_cache_lock);
+
+ udf_session->cache_line_read = malloc(UDF_READWRITE_LINE_LENGTH * sector_size);
+ udf_session->cache_line_write = malloc(UDF_READWRITE_LINE_LENGTH * sector_size);
+ assert(udf_session->cache_line_read);
+ assert(udf_session->cache_line_write);
+
+ bzero(udf_session->cache_write_callbacks, UDF_READWRITE_LINE_LENGTH * sizeof(struct udf_wrcallback));
+
+ return 0;
+}
+
+
+void udf_sync_session_cache(struct udf_session *udf_session) {
+ UDF_MUTEX_LOCK(&udf_session->session_cache_lock);
+ /* hmm... have to write out current write-cache */
+ udf_writeout_session_cache(udf_session);
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+}
+
+
+int udf_sync_caches(struct udf_log_vol *udf_log_vol) {
+ struct udf_volumeset *udf_volumeset;
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ uint32_t part_num;
+
+ /* XXX need to force writeout of session caches... XXX */
+ /* process all `partions->sessions' */
+
+ DEBUG(
+ printf("SYNC statistics\n");
+ printf("\tbufcache lru_len_data %d\n", udf_bufcache->lru_len_data);
+ printf("\tbufcache lru_len_metadata %d\n", udf_bufcache->lru_len_metadata);
+ printf("\tbufcache claimed/released %d\n", udf_bufcache->bcnt);
+ );
+
+ udf_volumeset = udf_log_vol->primary->volumeset;
+ SLIST_FOREACH(udf_part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ part_num = udf_part_mapping->udf_virt_part_num;
+
+ SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) {
+ if (udf_rw16(udf_partition->partition->part_num) == part_num) {
+ /* sync session */
+ DEBUG(printf("Syncing session cache for vpart %d, part %d\n", part_num, udf_partition->udf_session->session_num));
+ udf_sync_session_cache(udf_partition->udf_session);
+ }
+ }
+ }
+ return 0;
+}
+
+
+/******************************************************************************************
+ *
+ * Session and logvol sector reading/writing (simple caching)
+ *
+ ******************************************************************************************/
+
+int udf_read_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *buffer, int prefetch_sectors, int rwflags) {
+ uint32_t eff_sector, bit, sector_size;
+ int32_t cache_diff;
+ int error;
+
+ rwflags = rwflags; /* unused here */
+
+ /* maximise 'prefetch_sectors' to cache line length */
+ prefetch_sectors = MIN(UDF_READWRITE_LINE_LENGTH, prefetch_sectors);
+ sector_size = udf_session->disc->sector_size;
+
+ /* XXX cache coherency ???? XXX */
+ UDF_MUTEX_LOCK(&udf_session->session_cache_lock);
+
+ eff_sector = udf_session->session_offset + sector;
+
+ /* snoop write cache */
+ cache_diff = eff_sector - udf_session->cache_line_w_start;
+
+ if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) {
+ bit = (1 << cache_diff);
+ if (udf_session->cache_line_w_present & bit) {
+ /* return cached value */
+ memcpy(buffer, udf_session->cache_line_write + cache_diff * sector_size, sector_size);
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+ return 0;
+ }
+ /* not present */
+ }
+
+ /* check read cache */
+ cache_diff = eff_sector - udf_session->cache_line_r_start;
+
+ if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) {
+ bit = (1 << cache_diff);
+ if (udf_session->cache_line_r_present & bit) {
+ /* return cached value */
+ memcpy(buffer, udf_session->cache_line_read + cache_diff * sector_size, sector_size);
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+ return 0;
+ }
+ /* not present */
+ }
+
+ /* read in from this sector on for the prefetch length */
+ /* XXX use `pending' and `unalloc'/`freed' allocentry queue to minimise read/write misses in streams ? XXX */
+ /* XXX use 3 write streams ? XXX */
+ error = udf_read_physical_sectors(udf_session->disc, eff_sector, prefetch_sectors, what, udf_session->cache_line_read);
+ if (!error) {
+ udf_session->cache_line_r_start = eff_sector;
+ memcpy(buffer, udf_session->cache_line_read, sector_size);
+ udf_session->cache_line_r_present = 0;
+ for (cache_diff=0; cache_diff < prefetch_sectors; cache_diff++) {
+ bit = (1 << cache_diff);
+ udf_session->cache_line_r_present |= bit;
+ }
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+ return 0;
+ }
+
+ /* what now? */
+ DEBUG(
+ printf("ERROR! reading chunk\n");
+ );
+
+ udf_session->cache_line_r_present = 0;
+ error = udf_read_physical_sectors(udf_session->disc, eff_sector, 1, what, buffer);
+ if (!error) {
+ udf_session->cache_line_r_start = eff_sector;
+ udf_session->cache_line_r_present = 1;
+ }
+ DEBUG(
+ if (error) printf("ERROR reading sector %d\n", eff_sector)
+ );
+
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+
+ return error;
+}
+
+
+int udf_writeout_session_cache(struct udf_session *udf_session) {
+ struct udf_wrcallback *callback;
+ uint32_t bit, write_bits, error_bits, sector_size;
+ uint32_t num_sectors;
+ int32_t cache_diff;
+ uint32_t start_sector;
+ uint8_t *from, *to;
+ int error, report_error;
+
+ if (udf_session->cache_line_w_dirty == 0) return 0;
+
+ error_bits = 0;
+ sector_size = udf_session->disc->sector_size;
+ num_sectors = UDF_READWRITE_LINE_LENGTH;
+ start_sector = 0;
+ error = 0;
+ report_error = 0;
+
+ if (udf_session->disc->strict_overwrite) {
+ /* Have to do our own Read-Modify-Write :( */
+ assert((udf_session->cache_line_w_start % UDF_READWRITE_LINE_LENGTH) == 0);
+
+ /* all present ? */
+ if (udf_session->cache_line_w_dirty && (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT)) {
+ /* could snoop read buffer for missed sectors */
+ }
+
+ /* double check all present */
+ if (udf_session->cache_line_w_dirty && (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT)) {
+ /* read in from media :-S */
+ udf_session->cache_line_r_present = 0;
+ error = udf_read_physical_sectors(udf_session->disc, udf_session->cache_line_w_start, UDF_READWRITE_LINE_LENGTH, "cache line", udf_session->cache_line_read);
+ if (error) {
+ /* TODO try to fix-up please */
+ printf("Error reading physical sectors for cache for line_w_start %d ? : %s\n", udf_session->cache_line_w_start, strerror(error));
+ }
+ assert(!error);
+ udf_session->cache_line_r_start = udf_session->cache_line_w_start;
+ udf_session->cache_line_r_present = UDF_READWRITE_ALL_PRESENT;
+
+ for (cache_diff = 0; cache_diff < UDF_READWRITE_LINE_LENGTH; cache_diff++) {
+ bit = (1 << cache_diff);
+ if ((udf_session->cache_line_w_present & bit) == 0) {
+ from = udf_session->cache_line_read + cache_diff * sector_size;
+ to = udf_session->cache_line_write + cache_diff * sector_size;
+ memcpy(to, from, sector_size);
+ }
+ udf_session->cache_line_w_present |= bit;
+ }
+ }
+ assert(udf_session->cache_line_w_present == UDF_READWRITE_ALL_PRESENT);
+ }
+ assert(udf_session->cache_line_w_dirty);
+
+ if (udf_session->cache_line_w_present != UDF_READWRITE_ALL_PRESENT) {
+ /* count number of sectors present * (SEQUENTIAL?) */
+ start_sector = 0;
+ write_bits = 0;
+ cache_diff = 0;
+
+ DEBUG(printf("Writing out non complete line\n"));
+ DEBUG(printf("present %032o\n", udf_session->cache_line_w_present));
+ /* write out individual sectors */
+ while (cache_diff < UDF_READWRITE_LINE_LENGTH) {
+ bit = (1 << cache_diff);
+ if (udf_session->cache_line_w_present & bit) {
+ start_sector = cache_diff;
+ num_sectors = 1;
+
+ /* calculate memory address and disc address */
+ from = udf_session->cache_line_write + start_sector * sector_size;
+ start_sector += udf_session->session_offset + udf_session->cache_line_w_start;
+
+ /* write! */
+ error = udf_write_physical_sectors(udf_session->disc, start_sector, num_sectors, "cache line (bits)", from);
+ if (error) {
+ error_bits |= bit;
+ report_error = error;
+ } else {
+ udf_session->cache_line_w_dirty &= ~bit;
+ }
+ }
+ cache_diff++;
+ }
+ } else {
+ /* All present : calculate memory address and disc address */
+ from = udf_session->cache_line_write + start_sector * sector_size;
+ start_sector += udf_session->session_offset + udf_session->cache_line_w_start;
+
+ /* write! */
+ assert(num_sectors == UDF_READWRITE_LINE_LENGTH);
+ error = udf_write_physical_sectors(udf_session->disc, start_sector, num_sectors, "cache line", from);
+ if (error) {
+ error_bits = UDF_READWRITE_ALL_PRESENT;
+ } else {
+ udf_session->cache_line_w_dirty = 0;
+ }
+ report_error = error;
+ }
+
+ if (error_bits) {
+ /* ABORT/ROLLBACK */
+ for (cache_diff = 0; cache_diff < UDF_READWRITE_LINE_LENGTH; cache_diff++) {
+ bit = (1 << cache_diff);
+ if (error_bits & bit) {
+ from = udf_session->cache_line_write + cache_diff * sector_size;
+ callback = &udf_session->cache_write_callbacks[cache_diff];
+
+ udf_session->cache_line_w_present &= ~bit;
+
+ if (callback->function) {
+ callback->function(UDF_WRCALLBACK_REASON_ANULATE, callback, report_error, from);
+ } else {
+ fprintf(stderr, "WARNING: error encountered with NULL callback function\n");
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+
+/* XXX called directly OR called by purging dirty buffers out trough VOP_STRATEGY or trough VOP_INACTIVE XXX */
+int udf_write_session_sector(struct udf_session *udf_session, uint32_t sector, char *what, uint8_t *source, int rwflags, struct udf_wrcallback *wrcallback) {
+ uint32_t eff_sector, bit, sector_size;
+ int32_t cache_diff;
+ int error;
+
+ rwflags = rwflags; /* unused here */
+ what = what; /* unused for now */
+
+ assert(udf_session);
+ assert(udf_session->cache_line_read);
+ assert(udf_session->cache_line_write);
+ sector_size = udf_session->disc->sector_size;
+
+ /* XXX cache coherency ???? XXX */
+ error = 0;
+ UDF_MUTEX_LOCK(&udf_session->session_cache_lock);
+ eff_sector = udf_session->session_offset + sector;
+ cache_diff = eff_sector - udf_session->cache_line_w_start;
+
+ if (udf_session->cache_line_w_dirty && ((cache_diff < 0) || (cache_diff >= UDF_READWRITE_LINE_LENGTH))) {
+ /* hmm... have to write out current write-cache */
+ udf_writeout_session_cache(udf_session);
+ }
+
+ if (udf_session->cache_line_w_dirty == 0) {
+ if (udf_session->disc->strict_overwrite) {
+ udf_session->cache_line_w_start = eff_sector & ~(UDF_READWRITE_LINE_LENGTH-1);
+ } else {
+ udf_session->cache_line_w_start = eff_sector;
+ }
+ cache_diff = eff_sector - udf_session->cache_line_w_start;
+ udf_session->cache_line_w_present = 0;
+ }
+
+ if ((cache_diff >= 0) && (cache_diff < UDF_READWRITE_LINE_LENGTH)) {
+ /* its in the cache range: overwrite current value */
+ bit = (1 << cache_diff);
+ udf_session->cache_line_w_present |= bit;
+ udf_session->cache_line_w_dirty |= bit;
+ memcpy(udf_session->cache_line_write + cache_diff * sector_size, source, sector_size);
+ if (wrcallback)
+ memcpy(&udf_session->cache_write_callbacks[cache_diff], wrcallback, sizeof(struct udf_wrcallback));
+ else
+ bzero(&udf_session->cache_write_callbacks[cache_diff], sizeof(struct udf_wrcallback));
+ ;
+
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+ return 0;
+ }
+
+ UDF_MUTEX_UNLOCK(&udf_session->session_cache_lock);
+
+ return error;
+}
+
+
+/* reads in 'logvol->lb_size' logical sector size bytes */
+int udf_read_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, uint32_t prefetch_sectors, int rwflags) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ struct udf_session *udf_session;
+ uint64_t ses_off, trans_valid_len;
+ uint64_t offset;
+ uint32_t length, trans_length, trans_sectors, readahead;
+ uint32_t lb_size, sector_size;
+ uint32_t ses_sector, ses_offset;
+ int error, resolved;
+
+ lb_size = udf_log_vol->lb_size;
+ sector_size = udf_log_vol->sector_size;
+
+ DEBUG(
+ printf("Read logvol space for %s, from vpart %d, lb_num %d for logical sector size %d\n", what, vpart_num, (int)lb_num, (int) lb_size);
+ );
+
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition);
+ if (error) return error;
+
+ /* get the offset (in bytes) in the partition for translational purposes */
+ offset = (uint64_t) lb_num * lb_size;
+ length = lb_size;
+
+ udf_session = udf_partition->udf_session;
+ do {
+ trans_length = length;
+ resolved = 0;
+ ses_sector = 0;
+
+ /* TODO optimalisation: could use `trans_valid_len' and `prefetch_sectors' */
+ /* determine the translated address and its translation validity length */
+ error = udf_vpartoff_to_sessionoff(udf_log_vol, udf_part_mapping, udf_partition, offset, &ses_off, &trans_valid_len);
+ if (error) break;
+
+ ses_sector = ses_off / sector_size;
+ ses_offset = ses_off % sector_size; assert(ses_offset == 0);
+
+ trans_length = sector_size;
+ trans_sectors = 1;
+
+ /* estimate how much we could read-ahead given prefetch sectors and translation validation */
+ readahead = MIN(trans_valid_len, prefetch_sectors * lb_size);
+ readahead = (readahead + sector_size -1) / sector_size;
+
+ /* XXX could use partition_sector defs XXX */
+ error = udf_read_session_sector(udf_session, ses_sector, what, buffer + ses_offset, readahead, rwflags);
+ if (error) break;
+
+ /* advance to next block */
+ offset += trans_length;
+ length -= trans_length;
+ buffer += trans_length;
+ prefetch_sectors -= trans_sectors;
+
+ if (length == 0) return error;
+ } while (length && !error);
+
+ return EFAULT;
+}
+
+
+/* internal function; sector is allready a partition sector */
+void udf_fillin_fids_sector(uint8_t *buffer, uint32_t *fid_pos, uint32_t max_fidpos, uint32_t sector, uint32_t sector_size) {
+ struct fileid_desc *fid;
+ uint32_t rfid_pos;
+ uint32_t fid_len;
+
+ assert(fid_pos);
+ assert(buffer);
+
+ rfid_pos = (*fid_pos) % sector_size;
+ while (rfid_pos + sizeof(struct desc_tag) <= sector_size) {
+ if ((*fid_pos) + sizeof(struct desc_tag) > max_fidpos) {
+ return;
+ }
+
+ fid = (struct fileid_desc *) (buffer + (*fid_pos));
+ fid_len = udf_calc_tag_malloc_size((union dscrptr *) fid, sector_size);
+
+ /* update sector number and recalculate checkum */
+ fid->tag.tag_loc = udf_rw32(sector);
+ udf_validate_tag_sum((union dscrptr *) fid);
+
+ *fid_pos += fid_len;
+ rfid_pos += fid_len;
+ }
+}
+
+
+/* writes out 'logvol->lb_size' logical sector size bytes */
+/* XXX it ASSUMES that the translation is allready known/filled in (!) (offcource) XXX */
+int udf_write_logvol_sector(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, uint8_t *buffer, int rwflags, struct udf_wrcallback *wrcallback) {
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ struct udf_session *udf_session;
+ union dscrptr *dscrptr;
+ uint64_t ses_off, trans_valid_len;
+ uint64_t offset;
+ uint64_t length, trans_length;
+ uint32_t lb_size, sector_size;
+ uint32_t ses_sector, ses_offset;
+ uint32_t fid_pos, max_fid_pos;
+ int error, resolved, has_fids, recalc_crc, file_type;
+
+ lb_size = udf_log_vol->lb_size;
+ sector_size = udf_log_vol->sector_size;
+
+ DEBUG(
+ printf("Write logvol space for %s, rwflags = %d, from vpart %d, lb_num %d for logical sector size %d\n", what, rwflags, vpart_num, (int)lb_num, (int) lb_size);
+ );
+
+ error = udf_logvol_vpart_to_partition(udf_log_vol, vpart_num, &udf_part_mapping, &udf_partition);
+ if (error) return error;
+
+ /* get the offset (in bytes) in the partition for translational purposes */
+ offset = (uint64_t) lb_num * lb_size;
+ length = lb_size;
+
+ fid_pos = max_fid_pos = 0;
+ has_fids = recalc_crc = 0;
+ dscrptr = (union dscrptr *) buffer; /* doesn't have to be valid */
+
+ if (rwflags == UDF_C_FIDS) {
+ /* FIDs in this sector need to be updated, so search the first FID by using the resync function */
+ DEBUG(printf("C_FIDS\n"));
+ max_fid_pos = lb_size;
+ udf_resync_fid_stream(buffer, &fid_pos, max_fid_pos, &has_fids);
+ recalc_crc = 0;
+ }
+ if (rwflags == UDF_C_NODE) {
+ DEBUG(printf("C_NODE\n"));
+ /* if NODE with possibly an embedded FID stream -> have to patch up the FIDs (max one lbnum though) */
+ file_type = 0;
+ if (udf_rw16(dscrptr->tag.id) == TAGID_FENTRY) {
+ if ((udf_rw16(dscrptr->fe.icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == UDF_ICB_INTERN_ALLOC) {
+ DEBUG(printf("\tINTERN FE\n"));
+ fid_pos = (dscrptr->fe.data - buffer) + udf_rw32(dscrptr->fe.l_ea);
+ max_fid_pos = fid_pos + udf_rw64(dscrptr->fe.inf_len);
+ has_fids = 1;
+ recalc_crc = 1;
+ file_type = dscrptr->fe.icbtag.file_type; /* 8 bit */
+ }
+ } else {
+ if ((udf_rw16(dscrptr->fe.icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == UDF_ICB_INTERN_ALLOC) {
+ DEBUG(printf("\tINTERN EFE\n"));
+ fid_pos = (dscrptr->efe.data - buffer) + udf_rw32(dscrptr->efe.l_ea);
+ max_fid_pos = fid_pos + udf_rw64(dscrptr->efe.inf_len);
+ has_fids = 1;
+ recalc_crc = 1;
+ file_type = dscrptr->efe.icbtag.file_type; /* 8 bit */
+ }
+ }
+ if (!((file_type == UDF_ICB_FILETYPE_DIRECTORY) || (file_type == UDF_ICB_FILETYPE_STREAMDIR))) {
+ has_fids = 0;
+ }
+ }
+ DEBUG(
+ if (rwflags == UDF_C_USERDATA) {
+ printf("C_USERDATA\n");
+ }
+ printf("has_fids = %d, fid_pos = %d, max_fid_pos = %d\n", has_fids, fid_pos, max_fid_pos);
+ );
+
+ udf_session = udf_partition->udf_session;
+ do {
+ trans_length = length;
+ resolved = 0;
+ ses_sector = 0;
+
+ /* determine the translated address and its translation validity length */
+ error = udf_vpartoff_to_sessionoff(udf_log_vol, udf_part_mapping, udf_partition, offset, &ses_off, &trans_valid_len);
+ if (error) break;
+
+ ses_sector = ses_off / sector_size;
+ ses_offset = ses_off % sector_size; assert(ses_offset == 0);
+
+ /* FIDs need to be updated to include the correct physical sector */
+ if (has_fids) {
+ udf_fillin_fids_sector(buffer, &fid_pos, max_fid_pos, lb_num, sector_size);
+ if (recalc_crc) {
+ udf_validate_tag_and_crc_sums(dscrptr);
+ recalc_crc = 0;
+ }
+ }
+
+ /* XXX optimalisation: could use more of `trans_valid_len' XXX */
+ trans_length = sector_size;
+
+ /* XXX could use partition_sector defs XXX */
+ error = udf_write_session_sector(udf_session, ses_sector, what, buffer, rwflags, wrcallback);
+ if (error) break;
+
+ /* advance to next physical sector */
+ offset += trans_length;
+ length -= trans_length;
+ buffer += trans_length; /* really? */
+
+ DEBUG(
+ printf("write logvol sector loop: recalc_crc = %d, offset = %d, length = %d, buffer = %p\n", recalc_crc, (uint32_t) offset, (uint32_t) length, buffer);
+ );
+
+ if (length == 0) {
+ return error;
+ }
+ } while (length && !error);
+
+ return EFAULT;
+}
+
+
+/******************************************************************************************
+ *
+ * Descriptor readers and writers
+ *
+ ******************************************************************************************/
+
+
+/*
+ * Read in an descriptor in either logvol space or in session space determined
+ * by the specification of log_vol.
+ *
+ * In logvol space, lb_num specifies the logical block number in the logical
+ * volume wich can be bigger than a sector.
+ *
+ * In session space, lb_num specifies a distinct sector.
+ *
+ * The function returns the read in descriptor blob and its length; it deals
+ * with both short and long descriptors.
+ */
+
+int udf_read_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_session *udf_session, uint32_t lb_num, char *what, uint32_t cache_flags, union dscrptr **dscr, uint32_t *length) {
+ union dscrptr *cur_dscr, *new_dscr;
+ void *sector0;
+ uint32_t sector_size, num_sectors, sector;
+ uint32_t cur_length, new_length;
+ uint8_t *pos;
+ int error;
+
+ assert(dscr);
+ if (length) *length = 0;
+ *dscr = NULL;
+
+ assert((udf_log_vol && !udf_session) || (!udf_log_vol && udf_session));
+ sector_size = udf_log_vol ? udf_log_vol->lb_size : (uint32_t) udf_session->disc->sector_size;
+
+ /* All discriptors have a mimimum size of one sector be it logical or physical */
+ cur_length = sector_size;
+ num_sectors = 1;
+
+ sector0 = malloc(cur_length);
+ cur_dscr = sector0;
+ if (!sector0) {
+ printf("\t\t\tOut of memory claiming memory for %s\n", what);
+ return ENOMEM;
+ }
+
+ /* start reading in sector; read at offset 0 into the logic block */
+
+ if (udf_log_vol) {
+ /* could read more in advance? */
+ error = udf_read_logvol_sector(udf_log_vol, vpart_num, lb_num, what, (uint8_t *) cur_dscr, num_sectors, cache_flags);
+ } else {
+ error = udf_read_session_sector(udf_session, lb_num, what, (uint8_t *) cur_dscr, num_sectors, cache_flags);
+ }
+
+ if (!error) error = udf_check_tag(cur_dscr);
+ if (!error) {
+ new_length = udf_calc_tag_malloc_size(cur_dscr, sector_size);
+
+ DEBUG(
+ if (new_length < (uint32_t) udf_rw16(cur_dscr->tag.desc_crc_len) + UDF_DESC_TAG_LENGTH) {
+ printf("UDF warning: reading in %s for %d bytes but descriptor crc len is %d bytes\n", what, new_length,
+ udf_rw16(cur_dscr->tag.desc_crc_len) + UDF_DESC_TAG_LENGTH);
+ udf_dump_descriptor(cur_dscr);
+ }
+ );
+
+ if (new_length > cur_length) {
+ /* extent the current descriptor; length is multiple of (logical or session) sector size */
+ num_sectors = (new_length + sector_size -1) / sector_size;
+ new_length = num_sectors * sector_size;
+
+ new_dscr = malloc(new_length);
+ if (new_dscr) {
+ /* copy read-in stuff into the new allocated space */
+ memcpy(new_dscr, sector0, cur_length);
+ free(sector0);
+
+ /* read in the additional sectors */
+ cur_dscr = new_dscr;
+ cur_length = new_length;
+ for (sector = 1; sector < num_sectors; sector++) {
+ pos = ((uint8_t *) cur_dscr) + sector * sector_size;
+ if (udf_log_vol) {
+ /* could read more in advance? */
+ error = udf_read_logvol_sector(udf_log_vol, vpart_num, lb_num + sector, what, pos, num_sectors - sector, cache_flags);
+ } else {
+ error = udf_read_session_sector(udf_session, lb_num + sector, what, pos, num_sectors - sector, cache_flags);
+ }
+ }
+ } else {
+ free(sector0);
+ }
+ }
+ }
+ if (!error) {
+ *dscr = cur_dscr;
+ if (length) *length = cur_length; /* if requested return length */
+ error = udf_check_tag(*dscr);
+ if (!error) error = udf_check_tag_payload(*dscr);
+ }
+ return error;
+}
+
+
+/* Reads descriptor as in currenly recorded on disc or as is in the cache */
+int udf_read_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length) {
+ uint32_t cache_flags;
+
+ cache_flags = UDF_C_DSCR;
+ return udf_read_descriptor(NULL, 0, udf_session, lb_num, what, cache_flags, dscr, length);
+}
+
+
+/* Reads descriptor as in currenly recorded on disc or as is in the cache */
+int udf_read_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr **dscr, uint32_t *length) {
+ uint32_t cache_flags;
+
+ cache_flags = UDF_C_DSCR;
+ return udf_read_descriptor(udf_log_vol, vpart_num, NULL, lb_num, what, cache_flags, dscr, length);
+}
+
+
+static int udf_write_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, struct udf_session *udf_session, uint32_t lb_num, uint32_t dscr_lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) {
+ uint32_t dscr_length;
+ uint32_t sector_size;
+ uint32_t cur_length, sector, num_sectors;
+ uint8_t *pos;
+ int error, rwflags;
+
+ assert(dscr);
+
+ assert((udf_log_vol && !udf_session) || (!udf_log_vol && udf_session));
+ sector_size = udf_log_vol ? udf_log_vol->lb_size : (uint32_t) udf_session->disc->sector_size;
+
+ /* All discriptors have a mimimum size of one sector be it logical or physical */
+ cur_length = sector_size;
+ num_sectors = 1;
+
+ dscr_length = udf_calc_tag_malloc_size(dscr, sector_size);
+
+ /* extent the current descriptor; length is multiple of (logical or session) sector size */
+ num_sectors = (dscr_length + sector_size -1) / sector_size;
+
+ /* set the rwflags according to what kind of descriptor we are writing */
+ wrcallback->flags |= UDF_WRCALLBACK_FLAG_DESCRIPTOR; /* not needed? */
+ rwflags = UDF_C_DSCR;
+ if (udf_rw16(dscr->tag.id) == TAGID_FENTRY)
+ rwflags = UDF_C_NODE;
+ if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY)
+ rwflags = UDF_C_NODE;
+
+ /* write out descriptor */
+ error = 0;
+ if (udf_log_vol) {
+ /* prepare descriptor for writing */
+ dscr->tag.tag_loc = udf_rw32(dscr_lb_num);
+ udf_validate_tag_and_crc_sums(dscr);
+
+ /* write sectors */
+ for (sector = 0; sector < num_sectors; sector++) {
+ pos = ((uint8_t *) dscr) + sector * sector_size;
+
+ /* wrcallback->function is given */
+#if 0
+ wrcallback->udf_node =
+ wrcallback->lb_num = lb_num + sector;
+ wrcallback->length = sector_size;
+ wrcallback->vpart_num = vpart_num;
+#endif
+ DEBUG(printf("writing logical sector %8d for %s (sector offset %d)\n", lb_num + sector, what, sector));
+ error = udf_write_logvol_sector(udf_log_vol, vpart_num, lb_num + sector, what, pos, rwflags, wrcallback);
+ if (error) break;
+ }
+ } else {
+ /* prepare descriptor for writing */
+ dscr->tag.tag_loc = udf_rw32(dscr_lb_num);
+ udf_validate_tag_and_crc_sums(dscr);
+
+ /* write sectors */
+ for (sector = 0; sector < num_sectors; sector++) {
+ pos = ((uint8_t *) dscr) + sector * sector_size;
+ /* wrcallback->function is given */
+#if 0
+ wrcallback->lb_num = lb_num;
+ wrcallback->length = sector_size;
+#endif
+
+ DEBUG(printf("writing sector %8d for %s (sector offset %d)\n", lb_num + sector, what, sector));
+ error = udf_write_session_sector(udf_session, lb_num + sector, what, pos, rwflags, wrcallback);
+ if (error) break;
+ }
+ }
+
+ return error;
+}
+
+
+/* Write descriptor trough cache if present */
+int udf_write_session_descriptor(struct udf_session *udf_session, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) {
+ return udf_write_descriptor(NULL, 0, udf_session, lb_num, lb_num, what, dscr, wrcallback);
+}
+
+
+int udf_write_partition_descriptor(struct udf_partition *udf_partition, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) {
+ uint32_t dscr_lb_num;
+
+ dscr_lb_num = udf_rw32(lb_num + udf_partition->partition->start_loc);
+ return udf_write_descriptor(NULL, 0, udf_partition->udf_session, dscr_lb_num, lb_num, what, dscr, wrcallback);
+}
+
+
+/* Write descriptor trough cache if present */
+int udf_write_logvol_descriptor(struct udf_log_vol *udf_log_vol, uint32_t vpart_num, uint32_t lb_num, char *what, union dscrptr *dscr, struct udf_wrcallback *wrcallback) {
+ return udf_write_descriptor(udf_log_vol, vpart_num, NULL, lb_num, lb_num, what, dscr, wrcallback);
+}
+
+
+/* end of udf_readwrite.c */
+
diff --git a/udf_unix.c b/udf_unix.c
new file mode 100644
index 0000000..3a24b59
--- /dev/null
+++ b/udf_unix.c
@@ -0,0 +1,636 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_unix.c" is part of the UDFclient toolkit.
+ * File $Id: udf_unix.c,v 1.16 2015/08/05 18:26:31 reinoud Exp $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+/* XXX strip list to bare minimum XXX */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+
+#include "uscsilib.h"
+
+
+/* for locals */
+#include "udf.h"
+#include "udf_bswap.h"
+#include "udf_discop.h"
+#include "udf_unix.h"
+#include "uio.h"
+#include <pthread.h>
+
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+
+/* #define DEBUG(a) { a; } */
+#define DEBUG(a) if (0) { a; }
+
+
+/******************************************************************************************
+ *
+ * Bufcache emulation
+ *
+ ******************************************************************************************/
+
+/* shared bufcache structure */
+struct udf_bufcache *udf_bufcache = NULL;
+
+
+int udf_unix_init(void) {
+ if (udf_bufcache) {
+ fprintf(stderr, "reinit unix_init?\n");
+ return 0;
+ }
+
+ udf_bufcache = calloc(1, sizeof(struct udf_bufcache));
+ assert(udf_bufcache);
+
+ UDF_MUTEX_INIT(&udf_bufcache->bufcache_lock);
+
+ TAILQ_INIT(&udf_bufcache->lru_bufs_data);
+ TAILQ_INIT(&udf_bufcache->lru_bufs_metadata);
+
+ pthread_cond_init(&udf_bufcache->purgethread_signal, NULL);
+ pthread_mutex_init(&udf_bufcache->purgethread_lock, NULL);
+
+ pthread_cond_init(&udf_bufcache->processed_signal, NULL);
+ pthread_mutex_init(&udf_bufcache->processed_lock, NULL);
+
+ return 0;
+}
+
+
+/* delete the buf entry */
+void udf_free_buf_entry(struct udf_buf *buf_entry) {
+ assert(udf_bufcache);
+
+ buf_entry->b_vp = NULL; /* detach, i.e. recycle */
+ buf_entry->b_flags = 0; /* just in case */
+
+udf_bufcache->bcnt--;
+ free(buf_entry->b_data);
+ free(buf_entry);
+}
+
+
+/* XXX knowledge of LBSIZE, FILETYPE, INTERNAL NODE (?) ! XXX */
+/* must be called with bufcache lock ! */
+int udf_get_buf_entry(struct udf_node *udf_node, struct udf_buf **buf_entry_p) {
+ struct udf_log_vol *log_vol;
+ struct udf_buf *buf_entry;
+ uint32_t lb_size;
+
+ assert(udf_node);
+ assert(udf_bufcache);
+ assert(buf_entry_p);
+
+ log_vol = udf_node->udf_log_vol;
+ lb_size = log_vol->lb_size;
+
+ *buf_entry_p = NULL;
+ buf_entry = NULL;
+
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) {
+ if (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) {
+ /* kick writeout of data; if past max wait for space to continue */
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ udf_purgethread_kick("Data buffer surplus");
+ while (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MAX) {
+ udf_purgethread_kick("Metadata buffer surplus");
+ /* wait for processed signal */
+ pthread_mutex_lock(&udf_bufcache->processed_lock);
+ pthread_cond_wait(&udf_bufcache->processed_signal, &udf_bufcache->processed_lock);
+ pthread_mutex_unlock(&udf_bufcache->processed_lock);
+ }
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ }
+ } else {
+ if (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) {
+ /* kick writeout of data; if past max wait for space to continue */
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ udf_purgethread_kick("Data buffer surplus");
+ while (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MAX) {
+ udf_purgethread_kick("Data buffer surplus");
+ /* wait for processed signal */
+ pthread_mutex_lock(&udf_bufcache->processed_lock);
+ pthread_cond_wait(&udf_bufcache->processed_signal, &udf_bufcache->processed_lock);
+ pthread_mutex_unlock(&udf_bufcache->processed_lock);
+ }
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ }
+ }
+
+ /* create new buf_entry */
+ buf_entry = calloc(1, sizeof(struct udf_buf));
+ if (!buf_entry) return ENOMEM;
+
+ buf_entry->b_data = calloc(1, lb_size);
+ if (!buf_entry->b_data) {
+ *buf_entry_p = NULL;
+ free(buf_entry);
+ return ENOMEM;
+ }
+ *buf_entry_p = buf_entry;
+
+ /* fill in details */
+ buf_entry->b_bufsize = lb_size;
+ buf_entry->b_bcount = 0;
+ buf_entry->b_resid = lb_size;
+ buf_entry->b_lblk = 0;
+ buf_entry->b_flags = B_INVAL;
+ buf_entry->b_vp = udf_node; /* not just NULL ? */
+
+udf_bufcache->bcnt++;
+ return 0;
+}
+
+
+/* really `out of the sky' hash formula */
+uint32_t udf_calc_bufhash(struct udf_node *udf_node, uint32_t b_lblk) {
+ return (udf_node->hashkey * 5 + b_lblk);
+}
+
+
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+void udf_mark_buf_needing_allocate(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ uint32_t lb_size;
+
+ assert(udf_node);
+ /* assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); */
+ lb_size = udf_node->udf_log_vol->lb_size;
+
+ /* if it isnt allready marked to eb allocated, allocate it and claim space */
+ if (!(buf_entry->b_flags & B_NEEDALLOC)) {
+ udf_node->udf_log_vol->await_alloc_space += lb_size;
+ buf_entry->b_flags |= B_NEEDALLOC;
+ }
+}
+
+
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+void udf_mark_buf_allocated(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ uint32_t lb_size;
+
+ assert(udf_node);
+ /* assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked); */
+ lb_size = udf_node->udf_log_vol->lb_size;
+
+ /* if it needed allocation, clear the flag and release the space */
+ if (buf_entry->b_flags & B_NEEDALLOC) {
+ udf_node->udf_log_vol->await_alloc_space -= lb_size;
+ buf_entry->b_flags &= ~B_NEEDALLOC;
+ }
+}
+
+
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+void udf_mark_buf_dirty(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ assert(udf_node);
+ assert(buf_entry);
+ assert(udf_node->buf_mutex.locked);
+ assert(udf_bufcache->bufcache_lock.locked);
+
+ if (buf_entry->b_flags & B_DIRTY)
+ return;
+
+ if (udf_node->addr_type == UDF_ICB_INTERN_ALLOC) {
+ udf_mark_buf_needing_allocate(udf_node, buf_entry); /* signal it needs allocation */
+ }
+
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) {
+ udf_bufcache->lru_len_dirty_metadata++;
+ } else {
+ udf_bufcache->lru_len_dirty_data++;
+ }
+ buf_entry->b_flags |= B_DIRTY;
+ udf_node->v_numoutput++;
+}
+
+
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+void udf_mark_buf_clean(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ assert(udf_node);
+ assert(buf_entry);
+ assert(udf_node->buf_mutex.locked);
+ assert(udf_bufcache->bufcache_lock.locked);
+
+ if ((buf_entry->b_flags & B_DIRTY) == 0)
+ return;
+
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) {
+ udf_bufcache->lru_len_dirty_metadata--;
+ } else {
+ udf_bufcache->lru_len_dirty_data--;
+ }
+ buf_entry->b_flags &= ~B_DIRTY;
+ assert(udf_node->v_numoutput >= 1);
+ udf_node->v_numoutput--;
+}
+
+
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+int udf_attach_buf_to_node(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ struct udf_buf_queue *lru_chain;
+ struct udf_log_vol *log_vol;
+ struct udf_buf *buf, *lbuf;
+ uint32_t hashkey, bucket;
+
+ assert(udf_node);
+ assert(buf_entry);
+ assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked);
+
+ log_vol = udf_node->udf_log_vol;
+ buf_entry->b_vp = udf_node;
+
+if (0) {
+ /*
+ * Insert ordered in list. In KERNEL: vnode->v_dirtyblkhd is a list
+ * that can be reverse-sorted. Ordering not used yet thus commented
+ * out.
+ */
+ lbuf = TAILQ_LAST(&udf_node->vn_bufs, udf_buf_queue);
+ if (lbuf) {
+ if (buf_entry->b_lblk > lbuf->b_lblk) {
+ TAILQ_INSERT_TAIL(&udf_node->vn_bufs, buf_entry, b_vnbufs);
+ } else {
+ buf = TAILQ_FIRST(&udf_node->vn_bufs);
+ while (buf->b_lblk < buf->b_lblk) {
+ buf = TAILQ_NEXT(buf, b_vnbufs);
+ }
+ assert((buf->b_lblk != buf_entry->b_lblk) && (buf->b_vp == udf_node));
+ TAILQ_INSERT_BEFORE(buf, buf_entry, b_vnbufs);
+ }
+ } else {
+ TAILQ_INSERT_HEAD(&udf_node->vn_bufs, buf_entry, b_vnbufs);
+ }
+} else {
+ TAILQ_INSERT_TAIL(&udf_node->vn_bufs, buf_entry, b_vnbufs);
+}
+
+ /* fill buf into the bufcache */
+ hashkey = udf_calc_bufhash(udf_node, buf_entry->b_lblk);
+ bucket = hashkey & UDF_BUFCACHE_HASHMASK;
+
+ DEBUG(
+ struct udf_buf *buf;
+
+ /* checking for doubles */
+ LIST_FOREACH(buf, &udf_bufcache->udf_bufs[bucket], b_hash) {
+ if ((buf->b_vp == udf_node) && (buf->b_lblk == buf_entry->b_lblk)) {
+ printf("DOUBLE hashnode in UDF_BUFS!?\n");
+ exit(1);
+ }
+ }
+ );
+
+ LIST_INSERT_HEAD(&udf_bufcache->udf_bufs[bucket], buf_entry, b_hash);
+
+ /* queue it in the lru chain */
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) {
+ lru_chain = &udf_bufcache->lru_bufs_metadata;
+ udf_bufcache->lru_len_metadata++;
+ } else {
+ lru_chain = &udf_bufcache->lru_bufs_data;
+ udf_bufcache->lru_len_data++;
+ }
+ TAILQ_INSERT_TAIL(lru_chain, buf_entry, b_lru);
+
+ return 0;
+}
+
+
+/* kind of brelse() ? */
+/* !!! needs to be called with bufcache and udf_node->buf_mutex lock !!! */
+int udf_detach_buf_from_node(struct udf_node *udf_node, struct udf_buf *buf_entry) {
+ struct udf_buf_queue *lru_chain;
+ uint32_t hashkey, bucket;
+
+ assert(udf_node);
+ assert(buf_entry);
+ assert(udf_node->buf_mutex.locked && udf_bufcache->bufcache_lock.locked);
+
+ /* remove from vnode admin */
+ TAILQ_REMOVE(&udf_node->vn_bufs, buf_entry, b_vnbufs);
+
+ /* please don't forget this one */
+ if (buf_entry->b_flags & B_DIRTY)
+ udf_node->v_numoutput--;
+
+ /* remove from buffer cache */
+ hashkey = udf_calc_bufhash(udf_node, buf_entry->b_lblk);
+ bucket = hashkey & UDF_BUFCACHE_HASHMASK;
+ LIST_REMOVE(buf_entry, b_hash);
+
+ /* remove from lru lists */
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) {
+ lru_chain = &udf_bufcache->lru_bufs_metadata;
+ TAILQ_REMOVE(lru_chain, buf_entry, b_lru);
+ udf_bufcache->lru_len_metadata--;
+ } else {
+ lru_chain = &udf_bufcache->lru_bufs_data;
+ TAILQ_REMOVE(lru_chain, buf_entry, b_lru);
+ udf_bufcache->lru_len_data--;
+ }
+
+ return 0;
+}
+
+
+/* bufcache lock has to be held! */
+int udf_lookup_node_buf(struct udf_node *udf_node, uint32_t lblk, struct udf_buf **buf_p) {
+ struct udf_buf_queue *lru_chain;
+ struct udf_buf *buf;
+ uint32_t hashkey, bucket;
+
+ assert(udf_node);
+ assert(udf_bufcache->bufcache_lock.locked);
+
+ *buf_p = NULL;
+
+ hashkey = udf_calc_bufhash(udf_node, lblk);
+ bucket = hashkey & UDF_BUFCACHE_HASHMASK;
+ LIST_FOREACH(buf, &udf_bufcache->udf_bufs[bucket], b_hash) {
+ if ((buf->b_vp == udf_node) && (buf->b_lblk == lblk)) {
+ *buf_p = buf;
+
+ lru_chain = &udf_bufcache->lru_bufs_data;
+ if (udf_node->udf_filetype != UDF_ICB_FILETYPE_RANDOMACCESS) lru_chain = &udf_bufcache->lru_bufs_metadata;
+
+#ifdef UDF_METADATA_LRU
+ TAILQ_REMOVE(lru_chain, buf, b_lru);
+ TAILQ_INSERT_TAIL(lru_chain, buf, b_lru);
+#endif
+
+ break; /* for each */
+ }
+ }
+
+ return 0;
+}
+
+
+void *udf_purger(void *arg) {
+ struct timespec wakeup;
+ struct udf_buf *buf_entry, *marker;
+ struct udf_node *udf_node;
+
+ arg = arg; /* paramter not used */
+ marker = calloc(1, sizeof(struct udf_buf));
+ assert(marker);
+
+ UDF_VERBOSE(printf("\tbufcache thread initialising\n"));
+ while (1) {
+ DEBUG(printf("UDF bufcache sync thread: waiting for lock\n"));
+ /*
+ * If we are not asked to finish up our writing, block to
+ * wait for more data. Signal the reader to continue just in
+ * case it is still stuck.
+ */
+ if (!udf_bufcache->finish_purgethread) {
+ do {
+ /* determine the time we want to wake up again * */
+ clock_gettime(CLOCK_REALTIME, &wakeup);
+ wakeup.tv_sec += UDF_BUFCACHE_IDLE_SECS;
+
+ /* ask for more requests */
+ pthread_cond_signal(&udf_bufcache->processed_signal);
+ pthread_mutex_lock(&udf_bufcache->purgethread_lock);
+ pthread_cond_timedwait(&udf_bufcache->purgethread_signal, &udf_bufcache->purgethread_lock, &wakeup);
+ pthread_mutex_unlock(&udf_bufcache->purgethread_lock);
+
+ if (!udf_bufcache->purgethread_kicked) {
+ /* UDF_VERBOSE_MAX(printf("\nUDF purger woke up due to timeout\n")); */
+
+ /* see if we would want to do something */
+ if (udf_bufcache->flushall) /* shouldn't happen */
+ break;
+ if (udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN)
+ break; /* we have something to do */
+ if (udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN)
+ break; /* we have something to do */
+ } /* else : we have been explicitly asked to do something */
+ } while (!udf_bufcache->purgethread_kicked && !udf_bufcache->finish_purgethread);
+ }
+ udf_bufcache->purgethread_kicked = 0;
+
+ DEBUG(printf("UDF read/write thread: got activate\n"));
+
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ /* writeout surplus of dirty buffers */
+ /* PURGE surplus bufs if possible */
+ if ((udf_bufcache->lru_len_data >= UDF_LRU_DATA_MIN) || udf_bufcache->flushall) {
+ /* getting too many dirty data buffers */
+ TAILQ_INSERT_HEAD(&udf_bufcache->lru_bufs_data, marker, b_lru);
+ while ((buf_entry = TAILQ_NEXT(marker, b_lru))) {
+ /* advance marker */
+ TAILQ_REMOVE(&udf_bufcache->lru_bufs_data, marker, b_lru);
+ TAILQ_INSERT_AFTER(&udf_bufcache->lru_bufs_data, buf_entry, marker, b_lru);
+
+ /* process buf_entry */
+ if ((buf_entry->b_flags & B_DIRTY) != 0) {
+ if ((udf_bufcache->lru_len_dirty_data >= UDF_READWRITE_LINE_LENGTH*2) || udf_bufcache->flushall) {
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ /* signal there is time/space ahead and write out buffer */
+ pthread_cond_signal(&udf_bufcache->processed_signal);
+ udf_writeout_file_buffer(buf_entry->b_vp, "dirty data buf", UDF_C_USERDATA, buf_entry);
+DEBUG(printf("."); fflush(stdout));
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ }
+ }
+
+ /* if there are too many, drop least used */
+ if (udf_bufcache->lru_len_data > UDF_LRU_DATA_MIN) {
+ TAILQ_FOREACH(buf_entry, &udf_bufcache->lru_bufs_data, b_lru) {
+ /* process buf_entry */
+ if ((buf_entry != marker) && ((buf_entry->b_flags & B_DIRTY) == 0)) {
+ /* lock node bufs (locking protocol) */
+ udf_node = buf_entry->b_vp;
+ if (udf_node) {
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_detach_buf_from_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ } else {
+ printf("\n\nWARNING: got a NULL udf_node freeing dataspace\n\n");
+ }
+ udf_free_buf_entry(buf_entry);
+
+ /* signal there is time/space ahead */
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ pthread_cond_signal(&udf_bufcache->processed_signal);
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+
+ break; /* mandatory leaving FOREACH */
+ }
+ if (udf_bufcache->lru_len_data < UDF_LRU_DATA_MIN)
+ break; /* foreach */
+ }
+ }
+ }
+ TAILQ_REMOVE(&udf_bufcache->lru_bufs_data, marker, b_lru);
+ }
+ if ((udf_bufcache->lru_len_metadata >= UDF_LRU_METADATA_MIN) || udf_bufcache->flushall) {
+ /* getting too many dirty metadata buffers */
+ TAILQ_INSERT_HEAD(&udf_bufcache->lru_bufs_metadata, marker, b_lru);
+ while ((buf_entry = TAILQ_NEXT(marker, b_lru))) {
+ /* advance marker */
+ TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, marker, b_lru);
+ TAILQ_INSERT_AFTER(&udf_bufcache->lru_bufs_metadata, buf_entry, marker, b_lru);
+
+ /* process buf_entry */
+ if ((buf_entry->b_flags & B_DIRTY) != 0) {
+ if ((udf_bufcache->lru_len_dirty_metadata >= UDF_READWRITE_LINE_LENGTH*2) || udf_bufcache->flushall) {
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ /* signal there is time/space ahead and writeout buffer */
+ pthread_cond_signal(&udf_bufcache->processed_signal);
+ udf_writeout_file_buffer(buf_entry->b_vp, "dirty metadata buf", UDF_C_FIDS, buf_entry);
+DEBUG(printf("+"); fflush(stdout));
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+ }
+ }
+
+ /* if there are too many, drop least used */
+ if (udf_bufcache->lru_len_metadata > UDF_LRU_METADATA_MIN) {
+ TAILQ_FOREACH(buf_entry, &udf_bufcache->lru_bufs_metadata, b_lru) {
+ /* process buf_entry */
+ if ((buf_entry != marker) && ((buf_entry->b_flags & B_DIRTY)) == 0) {
+ /* lock node bufs (locking protocol); don't drop metadata from `held' nodes */
+ udf_node = buf_entry->b_vp;
+ if (udf_node && ((!udf_node->hold) || udf_bufcache->flushall)) {
+ UDF_MUTEX_LOCK(&udf_node->buf_mutex);
+ udf_detach_buf_from_node(udf_node, buf_entry);
+ UDF_MUTEX_UNLOCK(&udf_node->buf_mutex);
+ udf_free_buf_entry(buf_entry);
+
+ /* signal there is time/space ahead */
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ pthread_cond_signal(&udf_bufcache->processed_signal);
+ UDF_MUTEX_LOCK(&udf_bufcache->bufcache_lock);
+
+ break; /* mandatory leaving FOREACH */
+ } else {
+ if (!udf_node) {
+ printf("\n\nWARNING: got a NULL udf_node freeing METAspace\n\n");
+ }
+#ifdef UDF_METADATA_LRU
+ TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, buf_entry, b_lru);
+ TAILQ_INSERT_TAIL(&udf_bufcache->lru_bufs_metadata, buf_entry, b_lru);
+#endif
+ }
+ }
+ }
+ }
+ }
+ TAILQ_REMOVE(&udf_bufcache->lru_bufs_metadata, marker, b_lru);
+ }
+ /* can only be used once */
+ udf_bufcache->flushall = 0;
+
+ UDF_MUTEX_UNLOCK(&udf_bufcache->bufcache_lock);
+ /* PURGE nodes? (or only on request?) */
+
+ /* if asked to quit break out of the loop */
+ if (udf_bufcache->finish_purgethread) break;
+ }
+
+ UDF_VERBOSE(printf("\tbufcache thread joining\n"));
+ pthread_exit(0); /* join */
+
+ /* not reached */
+ return NULL;
+}
+
+
+int udf_start_unix_thread(void) {
+ /*
+ * start up bufcache purger thread and crudely kick it into
+ * existence.
+ */
+
+ if (udf_bufcache->thread_active) {
+ fprintf(stderr,"\tlogvol bufcache thread asked to start again; ignoring\n");
+ return 0;
+ }
+
+ DEBUG(printf("\tstarting logvol bufcache thread\n"));
+
+ udf_bufcache->thread_active = 1;
+ pthread_create(&udf_bufcache->purgethread_id, NULL, udf_purger, NULL);
+ sleep(1);
+
+ DEBUG(printf("\n\n"));
+ return 0;
+}
+
+
+int udf_stop_unix_thread(void) {
+ /* stop all associated threads */
+ UDF_VERBOSE(printf("\tstopping bufcache thread\n"));
+
+ if (udf_bufcache->thread_active) {
+ udf_bufcache->purgethread_kicked = 1;
+ udf_bufcache->finish_purgethread = 1;
+ pthread_cond_signal(&udf_bufcache->purgethread_signal);
+ pthread_join(udf_bufcache->purgethread_id, NULL); /* wait for join */
+ }
+
+ udf_bufcache->thread_active = 0;
+ return 0;
+}
+
+
+int udf_purgethread_kick(char *why) {
+ /*
+ * Kick the cache purger into existence in case its not active and wait
+ * for it to signal there is space left.
+ */
+
+ DEBUG(printf("\npurgethread kick! because of %s\n", why));
+ udf_bufcache->purgethread_kicked = 1;
+ pthread_cond_signal(&udf_bufcache->purgethread_signal);
+
+ return 0;
+}
+
+/* end of udf_unix.c */
+
diff --git a/udf_unix.h b/udf_unix.h
new file mode 100644
index 0000000..a2b1bb5
--- /dev/null
+++ b/udf_unix.h
@@ -0,0 +1,165 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf.h" is part of the UDFclient toolkit.
+ * File $Id: udf_unix.h,v 1.5 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef _UDF_UNIX_H_
+#define _UDF_UNIX_H_
+
+
+#include "udf.h"
+
+
+/* Predefines */
+struct udf_pri_vol;
+struct udf_node;
+
+
+/*
+ * Provides :: system buffer structure for file/metadata caching
+ *
+ * ... free after struct buf ...
+ */
+
+struct udf_buf {
+ UDF_MUTEX(b_interlock);
+ uint32_t b_lblk; /* logical block number for vnode */
+
+ struct udf_node *b_vp; /* points to its parent udf_node */
+ uint32_t b_flags; /* B_* flags */
+
+ uint8_t *b_data; /* buffer itself */
+ uint8_t *b_private; /* private pointer for FS */
+ uint32_t b_bufsize; /* allocated size */
+ uint32_t b_bcount; /* size used */
+ uint32_t b_resid; /* size remaining */
+
+ LIST_ENTRY(udf_buf) b_hash;
+ TAILQ_ENTRY(udf_buf) b_vnbufs; /* used for vnode bufs */
+ TAILQ_ENTRY(udf_buf) b_lru; /* lru queue (reuse) */
+};
+
+
+TAILQ_HEAD(udf_buf_queue, udf_buf);
+
+
+/*
+ * These flags are kept in b_flags. Copied from NetBSD's <sys/buf.h>
+ */
+#define B_AGE 0x00000001 /* Move to age queue when I/O done. */
+#define B_ASYNC 0x00000004 /* Start I/O, do not wait. */
+#define B_BAD 0x00000008 /* Bad block revectoring in progress. */
+#define B_BUSY 0x00000010 /* I/O in progress. */
+#define B_SCANNED 0x00000020 /* Block already pushed during sync */
+#define B_CALL 0x00000040 /* Call b_iodone from biodone. */
+#define B_DELWRI 0x00000080 /* Delay I/O until buffer reused. */
+#define B_DIRTY 0x00000100 /* Dirty page to be pushed out async. */
+#define B_DONE 0x00000200 /* I/O completed. */
+#define B_EINTR 0x00000400 /* I/O was interrupted */
+#define B_ERROR 0x00000800 /* I/O error occurred. */
+#define B_GATHERED 0x00001000 /* LFS: already in a segment. */
+#define B_INVAL 0x00002000 /* Does not contain valid info. */
+#define B_LOCKED 0x00004000 /* Locked in core (not reusable). */
+#define B_NOCACHE 0x00008000 /* Do not cache block after use. */
+#define B_CACHE 0x00020000 /* Bread found us in the cache. */
+#define B_PHYS 0x00040000 /* I/O to user memory. */
+#define B_RAW 0x00080000 /* Set by physio for raw transfers. */
+#define B_READ 0x00100000 /* Read buffer. */
+#define B_TAPE 0x00200000 /* Magnetic tape I/O. */
+#define B_WANTED 0x00800000 /* Process wants this buffer. */
+#define B_WRITE 0x00000000 /* Write buffer (pseudo flag). */
+#define B_XXX 0x02000000 /* Debugging flag. */
+#define B_VFLUSH 0x04000000 /* Buffer is being synced. */
+#define B_NEEDALLOC 0x08000000 /* TEMP */
+
+#define BUF_FLAGBITS \
+ "\20\1AGE\3ASYNC\4BAD\5BUSY\6SCANNED\7CALL\10DELWRI" \
+ "\11DIRTY\12DONE\13EINTR\14ERROR\15GATHERED\16INVAL\17LOCKED\20NOCACHE" \
+ "\22CACHE\23PHYS\24RAW\25READ\26TAPE\30WANTED\32XXX\33VFLUSH"
+
+
+
+
+
+struct udf_bufcache {
+ /* udf_node buf's multiplexed in one hashtable on (inode, b_lblk) */
+ LIST_HEAD(bufcache, udf_buf) udf_bufs[UDF_BUFCACHE_HASHSIZE];
+
+ UDF_MUTEX(bufcache_lock);
+
+ int32_t bcnt; /* temp var counting alloc/free */
+
+ uint32_t lru_len_data, lru_len_metadata;
+ uint32_t lru_len_dirty_data, lru_len_dirty_metadata;
+ struct udf_buf_queue lru_bufs_data;
+ struct udf_buf_queue lru_bufs_metadata;
+
+ /* thread support for bufs & nodes purge (bufcache emulation) */
+ pthread_t purgethread_id;
+ pthread_mutex_t purgethread_lock; /* lock the thread code out */
+ pthread_cond_t purgethread_signal; /* signal there is work to be done */
+ int purgethread_kicked; /* sanity for spurious wakeups */
+
+ pthread_mutex_t processed_lock; /* lock for main threads to wait on */
+ pthread_cond_t processed_signal; /* signals an action has been done */
+
+ int thread_active;
+ int finish_purgethread; /* ask thread to stop what its doing and exit */
+ int flushall; /* flusha all dirty buffers */
+};
+
+
+extern struct udf_bufcache *udf_bufcache;
+
+
+/* bufcache emulation */
+extern int udf_get_buf_entry(struct udf_node *udf_node, struct udf_buf **buf_entry_p);
+extern void udf_free_buf_entry(struct udf_buf *buf_entry);
+extern int udf_attach_buf_to_node(struct udf_node *udf_node, struct udf_buf *buf_entry);
+extern int udf_detach_buf_from_node(struct udf_node *udf_node, struct udf_buf *buf_entry);
+extern int udf_build_udf_node(struct udf_node *dir_node, struct fileid_desc *fid, struct udf_node **res_sub_node);
+extern int udf_lookup_node_buf(struct udf_node *udf_node, uint32_t lblk, struct udf_buf **buf_p);
+
+extern void udf_mark_buf_clean(struct udf_node *udf_node, struct udf_buf *buf_entry);
+extern void udf_mark_buf_dirty(struct udf_node *udf_node, struct udf_buf *buf_entry);
+
+/* maybe not strictly udf_unix */
+extern void udf_mark_buf_needing_allocate(struct udf_node *udf_node, struct udf_buf *buf_entry);
+extern void udf_mark_buf_allocated(struct udf_node *udf_node, struct udf_buf *buf_entry);
+
+extern int udf_unix_init(void);
+extern int udf_start_unix_thread(void);
+extern int udf_stop_unix_thread(void); /* HELP ! need good clear closedown */
+
+/* TEMP */
+extern int udf_purgethread_kick(char *why);
+
+
+#endif /* _UDF_UNIX_H_ */
+
diff --git a/udf_verbose.c b/udf_verbose.c
new file mode 100644
index 0000000..8a1680b
--- /dev/null
+++ b/udf_verbose.c
@@ -0,0 +1,1513 @@
+/* $NetBSD$ */
+
+/*
+ * File "udf_verbose.c" is part of the UDFclient toolkit.
+ * File $Id: udf_verbose.c,v 1.117 2015/08/05 18:26:31 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include "udf.h"
+#include "udf_bswap.h"
+
+
+/* globals */
+void udf_dump_id(char *prefix, int len, char *id, struct charspec *chsp);
+void udf_dump_long_ad(char *prefix, struct long_ad *adr);
+void udf_dump_descriptor(union dscrptr *dscrpt);
+void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping);
+
+
+void udf_dump_unimpl(union dscrptr *dscrpt, char *name) {
+ dscrpt = dscrpt;
+ printf("\t\t(unimplemented dump of `%s` tag)\n", name);
+}
+
+
+void udf_dump_desc(struct desc_tag *tag) {
+ printf("\tTAG: descriptor %d, serial_num %d at sector %d, crc length %d bytes\n",
+ udf_rw16(tag->id), udf_rw16(tag->serial_num), udf_rw32(tag->tag_loc), udf_rw16(tag->desc_crc_len));
+}
+
+
+void udf_dump_anchor(struct anchor_vdp *vdp) {
+ printf("\t\tAnchor\n");
+ printf("\t\t\tMain volume descriptor set at %d for %d bytes\n",
+ udf_rw32(vdp->main_vds_ex.loc), udf_rw32(vdp->main_vds_ex.len));
+ printf("\t\t\tReserve volume descriptor set at %d for %d bytes\n",
+ udf_rw32(vdp->reserve_vds_ex.loc), udf_rw32(vdp->reserve_vds_ex.len));
+}
+
+
+void udf_dump_disc_anchors(struct udf_discinfo *disc) {
+ int session;
+
+ printf("\nUDF Dump of disc in device %s\n", disc->dev->dev_name);
+ printf("UDF sessions : ");
+ for (session = 0; session < disc->num_sessions; session++) {
+ if (disc->session_is_UDF[session]) {
+ printf("Yes");
+#if 0
+ if (disc->session_quirks[session] & CD_SESS_QUIRK_SESSION_LOCAL) {
+ printf("(local)");
+ }
+#endif
+ printf(" ");
+ } else {
+ printf("No ");
+ }
+ }
+ printf("\n\n");
+ UDF_VERBOSE_TABLES(
+ struct udf_session *udf_session;
+ /* Dump anchors */
+ STAILQ_FOREACH(udf_session, &disc->sessions, next_session) {
+ printf("UDF session %d (lba %d + %d sectors) anchor dump : \n", udf_session->session_num,
+ (uint32_t) disc->session_start[udf_session->session_num], (uint32_t) udf_session->session_length);
+ udf_dump_descriptor((union dscrptr *) &udf_session->anchor);
+ }
+ )
+}
+
+
+char *udf_messy_unicode_conv(char *buf) {
+ static char out_buf[1024];
+ uint16_t *uni_pos, uni_char;
+ char *pos;
+
+ pos = out_buf;
+ uni_pos = (uint16_t *) buf;
+
+ while ((uni_char = *uni_pos++)) {
+ if (uni_char & 0xff00) uni_char='_';
+ *pos++ = uni_char;
+ }
+
+ return out_buf;
+}
+
+
+char *udf_get_osname(int os_class, int os_id) {
+ static char buffer[40];
+
+ switch (os_class) {
+ case 0 : return "undefined OS";
+ case 1 : return "DOS/Windows 3.x";
+ case 2 : return "OS/2";
+ case 3 : return "MacOS";
+ case 4 :
+ switch (os_id) {
+ case 0 : return "UNIX";
+ case 1 : return "IBM AIX";
+ case 2 : return "SunOS/Solaris";
+ case 3 : return "HP/UX";
+ case 4 : return "Silicon Graphics Irix";
+ case 5 : return "Linux";
+ case 6 : return "MKLinux";
+ case 7 : return "FreeBSD";
+ case 8 : return "NetBSD";
+ default :
+ sprintf(buffer, "unknown UNIX (%d)", os_id);
+ return buffer;
+ }
+ case 5 : return "MS Windows 9x";
+ case 6 : return "MS Windows NT";
+ case 7 : return "OS/400";
+ case 8 : return "BeOS";
+ case 9 : return "MS Windows CE";
+ default :
+ break;
+ }
+ sprintf(buffer, "unknown OS (%d, %d)", os_class, os_id);
+ return buffer;
+}
+
+
+void udf_dump_regid(char *prefix, struct regid *id, int regid_type) {
+ char buffer[UDF_REGID_ID_SIZE+1];
+ int cnt, version;
+ uint8_t *pos;
+
+ memcpy(buffer, id->id, UDF_REGID_ID_SIZE);
+ buffer[UDF_REGID_ID_SIZE] = 0;
+
+ printf("%s `%s`", prefix, buffer);
+ if (regid_type == UDF_REGID_NAME) {
+ printf("\n");
+ return;
+ }
+ printf(" (");
+ pos = id->id_suffix;
+ switch (regid_type) {
+ case UDF_REGID_DOMAIN :
+ version = udf_rw16(*((uint16_t *) pos));
+ printf("UDFv %x; ", version);
+ if ((pos[2]) & UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT) printf("HARD ");
+ if ((pos[2]) & UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT) printf("SOFT");
+ if (((pos[2]) & 3) == 0) printf("no");
+ printf(" write protect ");
+ if ((pos[2]) & ~3) printf("; also undefined flags 0x%d", pos[2] & ~3);
+ break;
+ case UDF_REGID_UDF :
+ version = udf_rw16(*((uint16_t *) pos));
+ printf("UDFv %x; ", version);
+ printf("%s ", udf_get_osname(pos[2], pos[3]));
+ break;
+ case UDF_REGID_IMPLEMENTATION :
+ printf("%s [", udf_get_osname(pos[0], pos[1]));
+ for(cnt=2; cnt < 8; cnt++) {
+ printf("%02x ", *pos++);
+ }
+ printf("]");
+ break;
+ case UDF_REGID_NAME :
+ break;
+ case UDF_REGID_APPLICATION :
+ default :
+ printf("[");
+ for(cnt=0; cnt < 8; cnt++) {
+ printf("%02x ", *pos++);
+ }
+ printf("]");
+ break;
+ }
+ printf(") (flags=%d)\n", id->flags);
+}
+
+
+void udf_dump_timestamp(char *prefix, struct timestamp *t) {
+ printf("%s (%4d %02d %02d at %02d:%02d:%02d.%02d.%02d.%02d)\n", prefix, udf_rw16(t->year), t->month, t->day,
+ t->hour, t->minute, t->second, t->centisec, t->hund_usec, t->usec);
+}
+
+
+#if 0
+void udf_dump_charspec(char *prefix, struct charspec *chsp) {
+ int cnt, ch;
+
+ printf("%s type CS%d (", prefix, chsp->type);
+ for (cnt=0; cnt<63; cnt++) {
+ ch = chsp->inf[cnt];
+ if (ch < 32 || ch > 126) {
+ printf(".");
+ } else {
+ printf("%c", ch);
+ }
+ }
+ printf(")\n");
+}
+#endif
+
+
+void udf_dump_sparing_table(struct udf_sparing_table *spt) {
+ struct spare_map_entry *sp_entry;
+ uint32_t entry, entries;
+
+ printf("\t\tSparing table descriptor\n");
+ udf_dump_regid("\t\t\tSparing table Id ", &spt->id, UDF_REGID_UDF);
+ printf("\t\t\tRelocation table entries %d\n", udf_rw16(spt->rt_l));
+ printf("\t\t\tSequence number %d\n", udf_rw32(spt->seq_num));
+ printf("\t\t\tMappings :");
+
+ entries = udf_rw16(spt->rt_l);
+ for(entry = 0; entry < entries; entry++) {
+ if (entry % 4 == 0) printf("\n\t\t\t\t");
+ sp_entry = &spt->entries[entry];
+ printf("[%08x -> %08x] ", udf_rw32(sp_entry->org), udf_rw32(sp_entry->map));
+ }
+ printf("\n");
+}
+
+
+void udf_dump_pri_vol(struct pri_vol_desc *pvd) {
+ struct extent_ad *ext;
+
+ printf("\t\tPrimary volume descriptor\n");
+ printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(pvd->seq_num));
+ printf("\t\t\tPrimary volume descriptor number %d\n", udf_rw32(pvd->pvd_num));
+ udf_dump_id("\t\t\tVolume Id ", 32, pvd->vol_id, &pvd->desc_charset);
+ printf("\t\t\tVolume sequence number %d\n", udf_rw16(pvd->vds_num));
+ printf("\t\t\tMaximum volume sequence number %d\n", udf_rw16(pvd->max_vol_seq));
+ printf("\t\t\tInterchange level %d\n", udf_rw16(pvd->ichg_lvl));
+ printf("\t\t\tMaximum interchange level %d\n", udf_rw16(pvd->max_ichg_lvl));
+ udf_dump_id("\t\t\tVolume set Id ", 128, pvd->volset_id, &pvd->desc_charset);
+ /* udf_dump_charspec("\t\t\tCharspec for this descriptor ", &pvd->desc_charset); */
+ /* udf_dump_charspec("\t\t\tCharspec for the explaination ", &pvd->explanatory_charset); */
+ ext = &pvd->vol_abstract;
+ printf("\t\t\tVolume abstract at %d for %d bytes\n", udf_rw32(ext->loc), udf_rw32(ext->len));
+ ext = &pvd->vol_copyright;
+ printf("\t\t\tVolume copyright at %d for %d bytes\n", udf_rw32(ext->loc), udf_rw32(ext->len));
+ udf_dump_regid("\t\t\tApplication id", &pvd->app_id, UDF_REGID_APPLICATION);
+ udf_dump_timestamp("\t\t\tTimestamp", &pvd->time);
+ udf_dump_regid("\t\t\tImplementator id", &pvd->imp_id, UDF_REGID_IMPLEMENTATION);
+ printf("\t\t\tPrevious volume descriptor sequence locator at sector %d\n", udf_rw32(pvd->prev_vds_loc));
+ printf("\t\t\tFlags %d\n", udf_rw16(pvd->flags));
+}
+
+
+void udf_dump_implementation_volume(struct impvol_desc *ivd) {
+ struct charspec *charspec;
+
+ printf("\t\tImplementation use volume descriptor\n");
+ printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(ivd->seq_num));
+ udf_dump_regid("\t\t\tImplementator identifier", &ivd->impl_id, UDF_REGID_UDF);
+
+ /* check on UDF implementation info ... */
+ if (strcmp((char *) ivd->impl_id.id, "*UDF LV Info") == 0) {
+ charspec = &ivd->_impl_use.lv_info.lvi_charset;
+ /* udf_dump_charspec("\t\t\tLV info charspec ", charspec); */
+ udf_dump_id("\t\t\tLogical volume identifier ", 128, ivd->_impl_use.lv_info.logvol_id, charspec);
+ udf_dump_id("\t\t\tLV info 1 ", 36, ivd->_impl_use.lv_info.lvinfo1, charspec);
+ udf_dump_id("\t\t\tLV info 2 ", 36, ivd->_impl_use.lv_info.lvinfo2, charspec);
+ udf_dump_id("\t\t\tLV info 3 ", 36, ivd->_impl_use.lv_info.lvinfo3, charspec);
+ udf_dump_regid("\t\t\tImplementation identifier", &ivd->_impl_use.lv_info.impl_id, UDF_REGID_IMPLEMENTATION);
+ }
+}
+
+
+char *udf_dump_partition_access_type(int type) {
+ switch (type) {
+ case UDF_ACCESSTYPE_PSEUDO_OVERWITE : return "Pseudo overwiteable";
+ case UDF_ACCESSTYPE_READ_ONLY : return "Read only";
+ case UDF_ACCESSTYPE_WRITE_ONCE : return "Write once";
+ case UDF_ACCESSTYPE_REWRITEABLE : return "Rewritable (blocked or with erase)";
+ case UDF_ACCESSTYPE_OVERWRITABLE : return "Overwritable";
+ }
+ return "Unknown partion access type";
+}
+
+
+void udf_dump_part(struct part_desc *pd) {
+ struct part_hdr_desc *part_hdr_desc;
+
+ printf("\t\tPartition descriptor\n");
+ printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(pd->seq_num));
+ printf("\t\t\tFlags %d\n", udf_rw16(pd->flags));
+ printf("\t\t\tPartition number %d\n", udf_rw16(pd->part_num));
+ udf_dump_regid("\t\t\tContents", &pd->contents, UDF_REGID_APPLICATION);
+ printf("\t\t\tAccessType %s\n", udf_dump_partition_access_type(udf_rw32(pd->access_type)));
+ printf("\t\t\tPartition starts at sector %u for %u sectors\n", udf_rw32(pd->start_loc), udf_rw32(pd->part_len));
+ udf_dump_regid("\t\t\tImplementator id", &pd->imp_id, UDF_REGID_IMPLEMENTATION);
+
+ printf("\t\t\tPartition contents use (file) descriptors:\n");
+ if (strncmp((char *) pd->contents.id, "+NSR0", 5) == 0) {
+ part_hdr_desc = &pd->pd_part_hdr;
+ printf("\t\t\t\tUnallocated space table at logic block %u for %u bytes\n",
+ udf_rw32(part_hdr_desc->unalloc_space_table.lb_num),
+ udf_rw32(part_hdr_desc->unalloc_space_table.len)
+ );
+ printf("\t\t\t\tUnallocated space bitmap at logic block %u for %u bytes\n",
+ udf_rw32(part_hdr_desc->unalloc_space_bitmap.lb_num),
+ udf_rw32(part_hdr_desc->unalloc_space_bitmap.len)
+ );
+ printf("\t\t\t\tPartition integrity table at logic block %u for %u bytes\n",
+ udf_rw32(part_hdr_desc->part_integrity_table.lb_num),
+ udf_rw32(part_hdr_desc->part_integrity_table.len)
+ );
+ printf("\t\t\t\tReusable (freed) space table at logic block %u for %u bytes\n",
+ udf_rw32(part_hdr_desc->freed_space_table.lb_num),
+ udf_rw32(part_hdr_desc->freed_space_table.len)
+ );
+ printf("\t\t\t\tReusable (freed) space bitmap at logic block %u for %u bytes\n",
+ udf_rw32(part_hdr_desc->freed_space_bitmap.lb_num),
+ udf_rw32(part_hdr_desc->freed_space_bitmap.len)
+ );
+ } else {
+ printf("\t\t\t\tWARNING: Unknown or unused contents\n");
+ }
+}
+
+
+void udf_dump_log_vol(struct logvol_desc *lvd) {
+ union udf_pmap *pmap;
+ uint8_t pmap_type, pmap_size;
+ uint8_t *pmap_pos;
+ uint32_t map, sparing_table;
+ uint32_t lb_size, packet_len;
+
+ lb_size = udf_rw32(lvd->lb_size);
+
+ printf("\t\tLogical volume descriptor\n");
+ printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(lvd->seq_num));
+ udf_dump_id("\t\t\tLogical volume id ", 128, lvd->logvol_id, &lvd->desc_charset);
+ printf("\t\t\tLogical block size %d\n", udf_rw32(lvd->lb_size));
+ udf_dump_regid("\t\t\tDomainId", &lvd->domain_id, UDF_REGID_DOMAIN);
+ udf_dump_long_ad("\t\t\tFileset descriptor at", &lvd->_lvd_use.fsd_loc);
+ printf("\t\t\tMap table length %d\n", udf_rw32(lvd->mt_l));
+ printf("\t\t\tNumber of part maps %d\n", udf_rw32(lvd->n_pm));
+ udf_dump_regid("\t\t\tImplementation id", &lvd->imp_id, UDF_REGID_IMPLEMENTATION);
+ printf("\t\t\tIntegrety sequence at %d for %d bytes\n",
+ udf_rw32(lvd->integrity_seq_loc.loc), udf_rw32(lvd->integrity_seq_loc.len));
+ printf("\t\t\tPartion maps follow\n");
+
+ pmap_pos = &lvd->maps[0];
+ for (map = 0; map < udf_rw32(lvd->n_pm); map++) {
+ pmap = (union udf_pmap *) pmap_pos;
+ pmap_type = pmap->data[0];
+ pmap_size = pmap->data[1];
+
+ printf("\t\t\t\tPartion map type %d length %d \n", pmap_type, pmap_size);
+ /* only pmap types 1 and pmap types 2 are to be used */
+ printf("\t\t\t\t\tLogical %d maps to ", map);
+ switch (pmap_type) {
+ case 1 :
+ printf("partition %d on volume seq. number %d directly\n",
+ udf_rw16(pmap->pm1.part_num), udf_rw16(pmap->pm1.vol_seq_num));
+ break;
+ case 2 :
+ printf("partition %d on volume seq. number %d using\n",
+ udf_rw16(pmap->pm2.part_num), udf_rw16(pmap->pm2.vol_seq_num));
+ udf_dump_regid("\t\t\t\t\tmapping type", &pmap->pm2.part_id, UDF_REGID_UDF);
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Virtual Partition", UDF_REGID_ID_SIZE) == 0) {
+ /* nothing to print... */
+ }
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Sparable Partition", UDF_REGID_ID_SIZE) == 0) {
+ packet_len = udf_rw16(pmap->pms.packet_len);
+ printf("\t\t\t\t\t\tPacket length %d sectors (%d bytes)\n",
+ packet_len, packet_len * lb_size);
+ printf("\t\t\t\t\t\tNumber of sparing tables %d\n", pmap->pms.n_st);
+ printf("\t\t\t\t\t\tSize of each sparing table %d\n", udf_rw32(pmap->pms.st_size));
+ if (pmap->pms.n_st) {
+ printf("\t\t\t\t\t\tSparing tables at sectors ");
+ for (sparing_table = 0; sparing_table < pmap->pms.n_st; sparing_table++) {
+ printf("%d ", udf_rw32(pmap->pms.st_loc[sparing_table]));
+ }
+ printf("\n");
+ }
+ }
+ if (strncmp((char *) pmap->pm2.part_id.id, "*UDF Metadata Partition", UDF_REGID_ID_SIZE) == 0) {
+ printf("\t\t\t\t\t\tMetadata is %sduplicated on disc\n", pmap->pmm.flags & METADATA_DUPLICATED ? "":"NOT ");
+ printf("\t\t\t\t\t\tAllocation unit size %d sectors\n", udf_rw32(pmap->pmm.alloc_unit_size));
+ printf("\t\t\t\t\t\tAlignment unit size %d sectors\n", udf_rw32(pmap->pmm.alignment_unit_size));
+ printf("\t\t\t\t\t\tMetadata file at part. sector %d\n", udf_rw32(pmap->pmm.meta_file_lbn));
+ if (udf_rw32(pmap->pmm.meta_mirror_file_lbn) != (uint32_t) -1)
+ printf("\t\t\t\t\t\tMetadata mirror file at part. sector %d\n", udf_rw32(pmap->pmm.meta_mirror_file_lbn));
+ if (udf_rw32(pmap->pmm.meta_bitmap_file_lbn) != (uint32_t) -1)
+ printf("\t\t\t\t\t\tMetadata bitmap file at part. sector %d\n", udf_rw32(pmap->pmm.meta_bitmap_file_lbn));
+ }
+ break;
+ default :
+ break;
+ }
+ pmap_pos += pmap_size;
+ }
+}
+
+
+void udf_dump_unalloc_space(struct unalloc_sp_desc *usd) {
+ struct extent_ad *alloc_desc;
+ uint32_t desc_num;
+
+ printf("\t\tUnallocated space descriptor\n");
+ printf("\t\t\tVolume descriptor sequence number %d\n", udf_rw32(usd->seq_num));
+ printf("\t\t\tNumber of free space slots %d\n", udf_rw32(usd->alloc_desc_num));
+ if (udf_rw32(usd->alloc_desc_num)) {
+ printf("\t\t\tFree space at : ");
+ for (desc_num = 0; desc_num < udf_rw32(usd->alloc_desc_num); desc_num++) {
+ alloc_desc = &usd->alloc_desc[desc_num];
+ printf("[%d %d] ", udf_rw32(alloc_desc->loc), udf_rw32(alloc_desc->loc)+udf_rw32(alloc_desc->len));
+ }
+ printf("\n");
+ }
+}
+
+
+void udf_dump_terminating_desc(union dscrptr *desc) {
+ desc = desc;
+
+ printf("\t\tTerminating descriptor\n");
+}
+
+
+void udf_dump_logvol_integrity(struct logvol_int_desc *lvid) {
+ struct udf_logvol_info *impl;
+ uint32_t part, num_part;
+ uint32_t *pos1, *pos2;
+ uint32_t free, size, rest_bytes;
+ uint32_t version;
+ const char *inttp;
+
+ printf("\t\tLogical volume integrity descriptor\n");
+ udf_dump_timestamp("\t\t\tTimestamp ", &lvid->time);
+
+ inttp = "UNKNOWN/INVALID";
+ if (udf_rw32(lvid->integrity_type) == UDF_INTEGRITY_CLOSED)
+ inttp = "closed";
+ if (udf_rw32(lvid->integrity_type) == UDF_INTEGRITY_OPEN)
+ inttp = "closed";
+ printf("\t\t\tIntegrity type %s\n", inttp);
+ printf("\t\t\tNext integrity sequence at %d for %d bytes\n",
+ udf_rw32(lvid->next_extent.loc), udf_rw32(lvid->next_extent.len));
+ printf("\t\t\tNext free unique file ID %d\n", (uint32_t) udf_rw64(lvid->lvint_next_unique_id));
+ printf("\t\t\tLength of implementation use area %d bytes\n", udf_rw32(lvid->l_iu));
+
+ num_part = udf_rw32(lvid->num_part);
+ printf("\t\t\tNumber of partitions %d\n", num_part);
+ for (part=0; part < num_part; part++) {
+ pos1 = &lvid->tables[0] + part;
+ pos2 = &lvid->tables[0] + num_part + part;
+ free = udf_rw32(*pos1);
+ size = udf_rw32(*pos2);
+ printf("\t\t\tPartition %d : %u blocks free space out of %u blocks\n", part, free, size);
+ }
+
+ /* printout the implementation use field */
+ impl = (struct udf_logvol_info *) (lvid->tables + 2*num_part);
+
+ udf_dump_regid("\t\t\tImplemenator Id", &impl->impl_id, UDF_REGID_IMPLEMENTATION );
+ printf("\t\t\tNumber of files %d\n", udf_rw32(impl->num_files));
+ printf("\t\t\tNumber of directories %d\n", udf_rw32(impl->num_directories));
+ version = udf_rw16(impl->min_udf_readver);
+ printf("\t\t\tMinimum readversion UDFv %x\n", version);
+ version = udf_rw16(impl->min_udf_writever);
+ printf("\t\t\tMinimum writeversion UDFv %x\n", version);
+ version = udf_rw16(impl->max_udf_writever);
+ printf("\t\t\tMaximum writeversion UDFv %x\n", version);
+ rest_bytes = udf_rw32(lvid->l_iu)-sizeof(struct udf_logvol_info);
+ if (rest_bytes > 0) printf("\t\t\t<%d bytes of undumped extra implementation use area>", rest_bytes);
+ printf("\n");
+}
+
+
+void udf_dump_fileset_desc(struct fileset_desc *fsd) {
+ printf("\t\tFileset descriptor\n");
+ udf_dump_timestamp("\t\t\tTimestamp ", &fsd->time);
+ printf("\t\t\tInterchange level %d\n", udf_rw16(fsd->ichg_lvl));
+ printf("\t\t\tMax interchange level %d\n", udf_rw16(fsd->max_ichg_lvl));
+ printf("\t\t\tCharset lists %d\n", udf_rw32(fsd->charset_list));
+ printf("\t\t\tMax charset lists %d\n", udf_rw32(fsd->max_charset_list));
+ printf("\t\t\tFileset number %d\n", udf_rw32(fsd->fileset_num));
+ printf("\t\t\tFileset descriptor number %d\n", udf_rw32(fsd->fileset_desc_num));
+ /* udf_dump_charspec("\t\t\tLogical volume id charspec ", &fsd->logvol_id_charset); */
+ /* udf_dump_charspec("\t\t\tFileset id charspec ", &fsd->fileset_charset); */
+ udf_dump_id("\t\t\tLogical volume id ", 128, fsd->logvol_id, &fsd->logvol_id_charset);
+ udf_dump_id("\t\t\tFileset id ", 32, fsd->fileset_id, &fsd->fileset_charset);
+ udf_dump_id("\t\t\tCopyright file id ", 32, fsd->copyright_file_id, &fsd->fileset_charset);
+ udf_dump_id("\t\t\tAbstract file id ", 32, fsd->abstract_file_id, &fsd->fileset_charset);
+ udf_dump_regid("\t\t\tDomainId", &fsd->domain_id, UDF_REGID_DOMAIN);
+ udf_dump_long_ad("\t\t\tRootdir ICB found ", &fsd->rootdir_icb);
+ udf_dump_long_ad("\t\t\tNext extend for fileset ", &fsd->next_ex);
+ udf_dump_long_ad("\t\t\tStreamdir ICB found ", &fsd->streamdir_icb);
+}
+
+
+void udf_dump_fileid_in_charspec(struct fileid_desc *fid, struct charspec *chsp) {
+ char *pos, file_char;
+
+ printf("\tFile id entry\n");
+ printf("\t\tFile version number %d\n", udf_rw16(fid->file_version_num));
+ file_char = fid->file_char;
+ printf("\t\tFile characteristics %d :\t", file_char);
+ if (file_char & UDF_FILE_CHAR_VIS) printf("hidden ");
+ if (file_char & UDF_FILE_CHAR_DEL) printf("deleted ");
+ if (file_char & UDF_FILE_CHAR_PAR) printf("parent(..) ");
+ if (file_char & UDF_FILE_CHAR_DIR) printf("directory ");
+ if (file_char & UDF_FILE_CHAR_META) printf("METADATA ");
+ printf("\n");
+ udf_dump_long_ad("\t\tFile ICB", &fid->icb);
+ printf("\t\tLength of file identifier area %d\n", fid->l_fi);
+ printf("\t\tOSTA UDF Unique ID %d\n", udf_rw32(fid->icb.impl.im_used.unique_id));
+ printf("\t\tOSTA UDF fileflags %d\n", udf_rw16(fid->icb.impl.im_used.flags));
+ printf("\t\tImplementation use length %d\n", udf_rw16(fid->l_iu));
+
+ if (udf_rw16(fid->l_iu)) {
+ /* Ecma 1/7.4 demands a (padded if wanted) implementation identifier */
+ if (udf_rw16(fid->l_iu) >= sizeof(struct regid)) {
+ udf_dump_regid("\t\t\tModified by", (struct regid *) &fid->data, UDF_REGID_IMPLEMENTATION);
+ } else {
+ printf("\t\t\tBROKEN fid, expected at least enough space for implementation regid\n");
+ }
+ }
+
+ pos = (char *) fid->data + udf_rw16(fid->l_iu);
+ if (file_char & UDF_FILE_CHAR_PAR) {
+ printf("\t\tParent directory ..\n");
+ } else {
+ udf_dump_id("\t\tFilename", fid->l_fi, pos, chsp);
+ }
+}
+
+
+void udf_dump_fileid(struct fileid_desc *fid) {
+ struct charspec chsp;
+
+ /* prolly OSTA compressed unicode anyway */
+ chsp.type = 0;
+ strcpy((char *) chsp.inf, "OSTA Compressed Unicode");
+
+ udf_dump_fileid_in_charspec(fid, &chsp);
+}
+
+
+void udf_dump_icb_tag(struct icb_tag *icb_tag) {
+ uint32_t flags, strat_param16;
+
+ flags = udf_rw16(icb_tag->flags);
+ strat_param16 = udf_rw16(* (uint16_t *) icb_tag->strat_param);
+ printf("\t\tICB Prior direct entries recorded (excl.) %d\n", udf_rw32(icb_tag->prev_num_dirs));
+ printf("\t\tICB Strategy type %d\n", udf_rw16(icb_tag->strat_type));
+ printf("\t\tICB Strategy type flags %d %d\n", icb_tag->strat_param[0], icb_tag->strat_param[1]);
+ printf("\t\tICB Maximum number of entries (non strat 4) %d\n", udf_rw16(icb_tag->max_num_entries));
+ printf("\t\tICB indirect entries/depth %d\n", strat_param16);
+ printf("\t\tICB File type %d\n", icb_tag->file_type);
+ printf("\t\tICB Parent ICB in logical block %d of mapped partition %d\n",
+ udf_rw32(icb_tag->parent_icb.lb_num), udf_rw16(icb_tag->parent_icb.part_num));
+ printf("\t\tICB Flags %d\n", udf_rw16(icb_tag->flags));
+ printf("\t\t\tFile/directory information using : ");
+ switch (flags & UDF_ICB_TAG_FLAGS_ALLOC_MASK) {
+ case UDF_ICB_SHORT_ALLOC :
+ printf("short allocation descriptor\n");
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ printf("long allocation descriptor\n");
+ break;
+ case UDF_ICB_EXT_ALLOC :
+ printf("extended allocation descriptor (out of specs)\n");
+ break;
+ case UDF_ICB_INTERN_ALLOC :
+ printf("internal in the ICB\n");
+ break;
+ }
+ if (icb_tag->file_type == UDF_ICB_FILETYPE_DIRECTORY)
+ if (flags & UDF_ICB_TAG_FLAGS_DIRORDERED)
+ printf("\t\t\tOrdered directory\n");
+ if (flags & UDF_ICB_TAG_FLAGS_NONRELOC) printf("\t\t\tNot relocatable\n");
+ printf("\t\t\tFile flags :");
+ if (flags & UDF_ICB_TAG_FLAGS_SETUID) printf("setuid() ");
+ if (flags & UDF_ICB_TAG_FLAGS_SETGID) printf("setgid() ");
+ if (flags & UDF_ICB_TAG_FLAGS_STICKY) printf("sticky ");
+ printf("\n");
+ if (flags & UDF_ICB_TAG_FLAGS_CONTIGUES)
+ printf("\t\t\tFile is contigues i.e. in one piece effectively \n");
+ if (flags & UDF_ICB_TAG_FLAGS_MULTIPLEVERS)
+ printf("\t\t\tExpect multiple versions of a file in this directory\n");
+}
+
+
+void udf_dump_indirect_entry(struct indirect_entry *inde) {
+ printf("\tIndirect (ICB) entry\n");
+ udf_dump_icb_tag(&inde->icbtag);
+ udf_dump_long_ad("\t\tPointing at", &inde->indirect_icb);
+ printf("\n");
+}
+
+
+void udf_dump_allocation_entries(uint8_t addr_type, uint8_t *pos, uint32_t data_length) {
+ union icb *icb;
+ uint32_t size, piece_length, piece_flags;
+ uint32_t entry;
+
+ entry = 0;
+ size = 0;
+ while (data_length) {
+ if (entry % 1 == 0) printf("\n\t");
+ printf(" [ ");
+ printf("blob at ");
+ /* what to do with strat type == 3 ? or is all set up ok then ? */
+ icb = (union icb *) pos;
+ switch (addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ piece_length = udf_rw32(icb->s_ad.len) & (((uint32_t) 1<<30)-1);
+ piece_flags = udf_rw32(icb->s_ad.len) >> 30; /* XXX ecma167 48.14.1.1 XXX */
+ printf("sector %8u for %8d bytes", udf_rw32(icb->s_ad.lb_num), piece_length);
+ if (piece_flags) printf(" flags %d", piece_flags);
+ size = sizeof(struct short_ad);
+ if (piece_length == 0) size = data_length;
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ piece_length = udf_rw32(icb->l_ad.len) & (((uint32_t) 1<<30)-1);
+ piece_flags = udf_rw32(icb->l_ad.len) >> 30; /* XXX ecma167 48.14.1.1 XXX */
+ printf("sector %8d for %8d bytes in logical partion %d", udf_rw32(icb->l_ad.loc.lb_num), piece_length,
+ udf_rw16(icb->l_ad.loc.part_num));
+ if (piece_flags) printf(" flags %d", piece_flags);
+ size = sizeof(struct long_ad);
+ if (piece_length == 0) size = data_length;
+ break;
+ case UDF_ICB_EXT_ALLOC :
+ printf("extended alloc (help)");
+ size = sizeof(struct ext_ad);
+ break;
+ case UDF_ICB_INTERN_ALLOC :
+ printf("internal blob here for %d bytes", data_length);
+ size = data_length;
+ break;
+ }
+ printf(" ] ");
+ entry++;
+ pos += size;
+ data_length -=size;
+ }
+ printf("\n");
+
+}
+
+
+/* TODO create a read-in/insert/cleanup etc. for extra attributes */
+void udf_dump_extattrseq(uint8_t *start, uint32_t offset, uint32_t impl_offset, uint32_t appl_offset, uint32_t length) {
+ struct impl_extattr_entry *impl_extattr;
+ struct appl_extattr_entry *appl_extattr;
+ struct filetimes_extattr_entry *filetimes_extattr;
+ struct device_extattr_entry *device_extattr;
+ struct vatlvext_extattr_entry *vatlvext_extattr;
+ struct extattr_entry *extattr;
+ struct timestamp *timestamp;
+ struct charspec chsp;
+ uint32_t extattr_len, au_l, iu_l, d_l;
+ uint32_t type, subtype, chksum, attr_space, print_attr_space;
+ uint32_t existence;
+ uint8_t *pos;
+ char *type_txt, what[256];
+ int is_free_ea_space, is_free_app_ea_space, is_vatlvext_space, bit;
+
+ /* if used its OSTA compressed unicode anyway */
+ chsp.type = 0;
+ strcpy((char *) chsp.inf, "OSTA Compressed Unicode");
+
+ /* if one of the offsets is `-1' (0xffffffff), it indicates that its not present; God i hate magic values */
+ if (impl_offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+ printf("\t\tNOTE: indicated no implementation related attributes are recorded in this extent\n");
+ if (appl_offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT)
+ printf("\t\tNOTE: indicated no application related attributes are recorded in this extent\n");
+
+ pos = start;
+ attr_space = UDF_REGID_UDF; /* really? */
+ while (length > 0) {
+ extattr = (struct extattr_entry *) pos;
+ extattr_len = udf_rw32(extattr->a_l);
+ type = udf_rw32(extattr->type);
+ subtype = extattr->subtype;
+
+ if (pos == start) printf("\t\tStart of extended file related attributes area\n");
+ if (offset == impl_offset) printf("\t\tStart of implementation related attributes area\n");
+ if (offset == appl_offset) printf("\t\tStart of application related attributes area\n");
+
+ if (pos == start) attr_space = UDF_REGID_UDF;
+ if (offset == impl_offset) attr_space = UDF_REGID_IMPLEMENTATION;
+ if (offset == appl_offset) attr_space = UDF_REGID_APPLICATION;
+
+ if (subtype != 1) printf("\t\t\tWARNING: unknown subtype %d\n", subtype);
+
+ print_attr_space = attr_space;
+ switch (type) {
+ case 65536 : /* [4/48.10.8] application use extended attributes */
+ appl_extattr = (struct appl_extattr_entry *) pos;
+ au_l = udf_rw32(appl_extattr->au_l);
+ printf("\t\t\tApplication use extended attribute\n");
+ if (attr_space != UDF_REGID_APPLICATION)
+ printf("\t\t\t\t*** application use extended attribute found in non application use area ***\n");
+ printf("\t\t\t\tLength of application use space %d\n", au_l);
+ udf_dump_regid("\t\t\t\tApplication use Id", &appl_extattr->appl_id, attr_space);
+ break;
+ case 2048 : /* [4/48.10.9] implementation use extended attributes */
+ impl_extattr = (struct impl_extattr_entry *) pos;
+ iu_l = udf_rw32(impl_extattr->iu_l);
+ chksum = udf_rw16(*((uint16_t *) impl_extattr->data));
+
+ printf("\t\t\tImplementation use extended attribute\n");
+ if (chksum != udf_ea_cksum(pos))
+ printf("\t\t\t\t*** header checksum failed (%d should be %d) ***\n", chksum, udf_ea_cksum(pos));
+ if (attr_space != UDF_REGID_IMPLEMENTATION)
+ printf("\t\t\t\t*** implementation use extended attribute found in non implementation use area ***\n");
+
+ if (strncmp((char *) impl_extattr->imp_id.id, "*UDF", 4) == 0)
+ print_attr_space = UDF_REGID_UDF;
+ printf("\t\t\t\tLength of implementation use space %d\n", iu_l);
+ udf_dump_regid("\t\t\t\tImplemenation use Id", &impl_extattr->imp_id, print_attr_space);
+ is_free_ea_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF FreeEASpace") == 0);
+ is_free_app_ea_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF FreeAppEASpace") == 0);
+ is_vatlvext_space = (strcmp((char *) impl_extattr->imp_id.id, "*UDF VAT LVExtension") == 0);
+ if (is_free_ea_space || is_free_app_ea_space) {
+ printf("\t\t\t\tFree space for new extended attributes (%d bytes total)\n", extattr_len);
+ } else if (is_vatlvext_space) {
+ vatlvext_extattr = (struct vatlvext_extattr_entry *) (impl_extattr->data + iu_l);
+ printf("\t\t\t\t\tUniqueID check %"PRIu64"\n", udf_rw64(vatlvext_extattr->unique_id_chk));
+ printf("\t\t\t\t\tNumber of files %d\n", udf_rw32(vatlvext_extattr->num_files));
+ printf("\t\t\t\t\tNumber of directories %d\n", udf_rw32(vatlvext_extattr->num_directories));
+ udf_dump_id("\t\t\t\t\tLogical volume id ", 128, vatlvext_extattr->logvol_id, &chsp);
+ } else {
+ printf("\t\t\t\t<Undumped %d bytes of implementation use data>\n", iu_l);
+ }
+ break;
+ case 1 : /* [4/48.10.3] : Character set information; UDF does allow/disallow explicitly */
+ printf("\t\t\tCharacter set information attribute\n");
+ printf("\t\t\t\t<Undumped %d bytes attribute>\n", extattr_len);
+ break;
+ case 3 : /* [4/48.10.4] : Alternate permissions; UDF 3.3.4.2: not to be recorded */
+ printf("\t\t\tAlternate permission attribute\n");
+ printf("\t\t\t\t<Undumped %d bytes attribute>\n", extattr_len);
+ break;
+ case 5 : /* [4/48.10.5] : File Times Extended Attribute */
+ case 6 : /* [4/48.10.6] : Information Times Extended Attribute; recorded in UDF ? */
+ /* ASSUMPTION : bit fields are not exlusive */
+ filetimes_extattr = (struct filetimes_extattr_entry *) pos;
+ d_l = udf_rw32(filetimes_extattr->d_l);
+ existence = udf_rw32(filetimes_extattr->existence);
+ type_txt = "File";
+ if (type == 6) type_txt = "File information";
+
+ printf("\t\t\t%s times extended attribute\n", type_txt);
+ timestamp = &filetimes_extattr->times[0];
+ for (bit = 0; bit < 32; bit++) {
+ if (d_l == 0) break;
+ if ((existence & (1 << bit)) == 0)
+ continue;
+ switch (bit) {
+ case 0 : /* File Creation Date and Time: the date and time of the day at which the file was created. */
+ sprintf(what, "\t\t\t\t%s created at ", type_txt);
+ break;
+ case 1 : /* Information Last Modification Date and Time: the date and time of the day at which the information in the file was last modified. */
+ sprintf(what, "\t\t\t\t%s last modified at ", type_txt);
+ break;
+ case 2 : /* File Deletion Date and Time: the date and time of the day after which the file may be deleted. */
+ sprintf(what, "\t\t\t\t%s may be deleted after ", type_txt);
+ break;
+ case 3 : /* File Effective Date and Time: the date and time of the day after which the file may be used. */
+ sprintf(what, "\t\t\t\t%s may only be used after ", type_txt);
+ break;
+ case 5 : /* File Last Backup Date and Time: the date and time of the day at which the file was last backed up. */
+ sprintf(what, "\t\t\t\t%s last backuped at ", type_txt);
+ break;
+ default : /* unspec */
+ sprintf(what, "\t\t\t\tUndefined meaning for %s time stamp ", type_txt);
+ break;
+ }
+ udf_dump_timestamp(what, timestamp);
+ d_l -= sizeof(struct timestamp);
+ timestamp++; /* advance */
+ }
+ break;
+ case 12 : /* [4/48.10.7] : Device Specification Extended Attribute */
+ device_extattr = (struct device_extattr_entry *) pos;
+ iu_l = udf_rw32(device_extattr->iu_l);
+ printf("\t\t\tDevice node extended attribute\n");
+ printf("\t\t\t\tMajor %d\n", udf_rw32(device_extattr->major));
+ printf("\t\t\t\tMinor %d\n", udf_rw32(device_extattr->minor));
+ if (iu_l >= sizeof(struct regid)) {
+ udf_dump_regid("\t\t\t\tImplementator", (struct regid *) (device_extattr->data), UDF_REGID_IMPLEMENTATION);
+ }
+ break;
+ default :
+ printf("\t\t\tUndumped extended attribute type %d\n", type);
+ printf("\t\t\t\tSubtype %d\n", subtype);
+ printf("\t\t\t\tLength %d\n", extattr_len);
+ break;
+ }
+ if (extattr_len == 0) {
+ printf("\t\t\tABORTing dump\n");
+ break;
+ }
+ pos += extattr_len;
+ offset += extattr_len;
+ length -= extattr_len;
+ }
+ printf("\n");
+}
+
+
+void udf_dump_extattr_hdr(struct extattrhdr_desc *eahd, uint32_t length) {
+ uint32_t hdr_len, impl_attr_loc, appl_attr_loc;
+ uint8_t *pos;
+
+ hdr_len = (uint32_t) sizeof(struct extattrhdr_desc);
+ impl_attr_loc = udf_rw32(eahd->impl_attr_loc);
+ appl_attr_loc = udf_rw32(eahd->appl_attr_loc);
+
+ printf("\t\tExtended attributes header:\n");
+ printf("\t\t\tLength %d bytes\n", length);
+ printf("\t\t\tImplementation attributes at offset %d\n", impl_attr_loc);
+ printf("\t\t\tApplication attributes at offset %d\n", appl_attr_loc);
+ printf("\t\t\tBytes remaining after header %d\n", length - hdr_len);
+
+ /* determine length of file related attributes space */
+ pos = (uint8_t *) eahd;
+ pos += hdr_len;
+ length -= hdr_len;
+
+ udf_dump_extattrseq(pos, hdr_len, impl_attr_loc, appl_attr_loc, length);
+}
+
+
+void udf_dump_file_entry(struct file_entry *fe) {
+ uint8_t *pos;
+ uint32_t length;
+ uint8_t addr_type;
+ uint32_t entries;
+ uint16_t strategy, strat_param16;
+
+ /* direct_entries = udf_rw32(fe->icbtag.prev_num_dirs); */
+ strat_param16 = udf_rw16(* (uint16_t *) (fe->icbtag.strat_param));
+ entries = udf_rw16(fe->icbtag.max_num_entries);
+ strategy = udf_rw16(fe->icbtag.strat_type);
+ addr_type = udf_rw16(fe->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+ printf("\tFile entry\n");
+ udf_dump_icb_tag(&fe->icbtag);
+ printf("\t\tUid %d\n", udf_rw32(fe->uid));
+ printf("\t\tGid %d\n", udf_rw32(fe->gid));
+ printf("\t\tPermissions %x\n", udf_rw32(fe->perm));
+ printf("\t\tLink count %d\n", udf_rw16(fe->link_cnt));
+ printf("\t\tRecord format %d\n", fe->rec_format);
+ printf("\t\tRecord display attributes %d\n", fe->rec_disp_attr);
+ printf("\t\tRecord length %d\n", fe->rec_len);
+ printf("\t\tInformation length %"PRIu64"\n", (uint64_t) udf_rw64(fe->inf_len));
+ printf("\t\tLogical blocks recorded %"PRIu64"\n", (uint64_t) udf_rw64(fe->logblks_rec));
+ udf_dump_timestamp("\t\tAccess time ", &fe->atime);
+ udf_dump_timestamp("\t\tModification time ", &fe->mtime);
+ udf_dump_timestamp("\t\tAttribute time ", &fe->attrtime);
+ printf("\t\tCheckpoint %d\n", udf_rw32(fe->ckpoint));
+ udf_dump_long_ad("\t\tExtended attributes ICB at", &fe->ex_attr_icb);
+ udf_dump_regid("\t\tImplementation", &fe->imp_id, UDF_REGID_IMPLEMENTATION);
+ printf("\t\tUniqueID %d\n", (uint32_t) udf_rw64(fe->unique_id));
+ printf("\t\tLength of extended attribute area %d\n", udf_rw32(fe->l_ea));
+ printf("\t\tLength of allocation descriptors %d\n", udf_rw32(fe->l_ad));
+
+ if (udf_rw32(fe->l_ea)) {
+ udf_dump_extattr_hdr((struct extattrhdr_desc *) &fe->data[0], udf_rw32(fe->l_ea));
+ }
+ if (udf_rw32(fe->ex_attr_icb.len)) {
+ printf("\t\t<Undumped %d bytes of extended attributes descriptor\n", udf_rw32(fe->ex_attr_icb.len));
+ }
+
+ printf("\t\tAllocation descriptors : \n");
+
+ pos = &fe->data[0] + udf_rw32(fe->l_ea);
+ length = udf_rw32(fe->l_ad);
+
+ udf_dump_allocation_entries(addr_type, pos, length);
+}
+
+
+void udf_dump_extfile_entry(struct extfile_entry *efe) {
+ uint8_t *pos;
+ uint32_t length;
+ uint8_t addr_type;
+ uint32_t entries;
+ uint16_t strategy, strat_param16;
+
+ /* direct_entries = udf_rw32(efe->icbtag.prev_num_dirs); */
+ strat_param16 = udf_rw16(* (uint16_t *) (efe->icbtag.strat_param));
+ entries = udf_rw16(efe->icbtag.max_num_entries);
+ strategy = udf_rw16(efe->icbtag.strat_type);
+ addr_type = udf_rw16(efe->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+
+ printf("\tExtended file entry\n");
+ udf_dump_icb_tag(&efe->icbtag);
+ printf("\t\tUid %d\n", udf_rw32(efe->uid));
+ printf("\t\tGid %d\n", udf_rw32(efe->gid));
+ printf("\t\tPermissions %x\n", udf_rw32(efe->perm));
+ printf("\t\tLink count %d\n", udf_rw16(efe->link_cnt));
+ printf("\t\tRecord format %d\n", efe->rec_format);
+ printf("\t\tRecord display attributes %d\n", efe->rec_disp_attr);
+ printf("\t\tRecord length %d\n", efe->rec_len);
+ printf("\t\tInformation length %"PRIu64"\n", (uint64_t) udf_rw64(efe->inf_len));
+ printf("\t\tObject size %"PRIu64"\n", (uint64_t) udf_rw64(efe->obj_size));
+ printf("\t\tLogical blocks recorded %"PRIu64"\n", (uint64_t) udf_rw64(efe->logblks_rec));
+ udf_dump_timestamp("\t\tAccess time ", &efe->atime);
+ udf_dump_timestamp("\t\tModification time ", &efe->mtime);
+ udf_dump_timestamp("\t\tCreation time ", &efe->ctime);
+ udf_dump_timestamp("\t\tAttribute time ", &efe->attrtime);
+ printf("\t\tCheckpoint %d\n", udf_rw32(efe->ckpoint));
+ udf_dump_long_ad("\t\tExtended attributes ICB at", &efe->ex_attr_icb);
+ udf_dump_long_ad("\t\tStreamdir ICB at", &efe->streamdir_icb);
+ udf_dump_regid("\t\tImplementation", &efe->imp_id, UDF_REGID_IMPLEMENTATION);
+ printf("\t\tUniqueID %d\n", (uint32_t) udf_rw64(efe->unique_id));
+ printf("\t\tLength of extended attribute area %d\n", udf_rw32(efe->l_ea));
+ printf("\t\tLength of allocation descriptors %d\n", udf_rw32(efe->l_ad));
+
+ if (udf_rw32(efe->l_ea)) {
+ udf_dump_extattr_hdr((struct extattrhdr_desc *) &efe->data[0], udf_rw32(efe->l_ea));
+ }
+ if (udf_rw32(efe->ex_attr_icb.len)) {
+ printf("\t\t<Undumped %d bytes of extended attributes descriptor\n", udf_rw32(efe->ex_attr_icb.len));
+ }
+
+ printf("\t\tAllocation descriptors : \n");
+
+ pos = &efe->data[0] + udf_rw32(efe->l_ea);
+ length = udf_rw32(efe->l_ad);
+
+ udf_dump_allocation_entries(addr_type, pos, length);
+}
+
+
+/* dump allocation extention descriptor */
+void udf_dump_alloc_extent(struct alloc_ext_entry *ext, int addr_type) {
+ uint8_t *pos;
+ uint32_t length;
+ int isshort, islong;
+
+ /* note we DONT know if its filled with short_ad's or long_ad's! */
+ printf("\tAllocation Extent descriptor\n");
+ printf("\t\tPrevious entry %d\n", udf_rw32(ext->prev_entry));
+ printf("\t\tLength of allocation descriptors %d\n", udf_rw32(ext->l_ad));
+
+ pos = &ext->data[0];
+ length = udf_rw32(ext->l_ad);
+
+ if (addr_type < 0) {
+ isshort = ((length % sizeof(struct short_ad)) == 0);
+ islong = ((length % sizeof(struct long_ad)) == 0);
+ if (isshort)
+ addr_type = UDF_ICB_SHORT_ALLOC;
+ if (islong)
+ addr_type = UDF_ICB_LONG_ALLOC;
+ if (!(isshort ^ islong)) {
+ printf("\t\tCan't determine if its filled with long_ad's or short_ad's !\n");
+ return;
+ }
+ }
+
+ udf_dump_allocation_entries(addr_type, pos, length);
+}
+
+
+/* dump a space table(entry) descriptor */
+void udf_dump_space_entry(struct space_entry_desc *sed) {
+ union icb *icb;
+ uint32_t addr_type, size, bytes;
+ uint32_t piece_sector, piece_length, piece_part;
+ uint8_t *pos;
+
+ printf("\tSpace entry table\n");
+ udf_dump_icb_tag(&sed->icbtag);
+ printf("\t\tSize in bytes %d\n", udf_rw32(sed->l_ad));
+
+ pos = &sed->entry[0];
+ bytes = udf_rw32(sed->l_ad);
+
+ addr_type = udf_rw16(sed->icbtag.flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK;
+ while (bytes) {
+ size = piece_length = piece_sector = piece_part = 0;
+ icb = (union icb *) pos;
+ switch (addr_type) {
+ case UDF_ICB_SHORT_ALLOC :
+ piece_length = udf_rw32(icb->s_ad.len) & (((uint32_t) 1<<31)-1);
+ piece_sector = udf_rw32(icb->s_ad.lb_num);
+ printf("[at sec %u for %d bytes] ", piece_sector, piece_length);
+ size = sizeof(struct short_ad);
+ break;
+ case UDF_ICB_LONG_ALLOC :
+ piece_length = udf_rw32(icb->l_ad.len) & (((uint32_t) 1<<31)-1);
+ piece_sector = udf_rw32(icb->l_ad.loc.lb_num);
+ piece_part = udf_rw16(icb->l_ad.loc.part_num);
+ size = sizeof(struct long_ad);
+ printf("[at sec %u for %d bytes at partition %d] ", piece_sector, piece_length, piece_part);
+ break;
+ case UDF_ICB_EXT_ALLOC :
+ case UDF_ICB_INTERN_ALLOC :
+ printf("\t\t\tWARNING : an internal alloc in a space entry?\n");
+ return;
+ }
+ bytes -= size;
+ }
+}
+
+
+/* dump a space bitmap descriptor */
+void udf_dump_space_bitmap(struct space_bitmap_desc *sbd) {
+ uint32_t bits, from, now, cnt;
+ uint8_t byte, bit, bitpos, state, *pos;
+
+ printf("\t\tSpace bitmap\n");
+ printf("\t\t\tNumber of bits %u\n", udf_rw32(sbd->num_bits));
+ printf("\t\t\tNumber of bytes %u\n", udf_rw32(sbd->num_bytes));
+ printf("\t\t\tMarked parts at :\n");
+
+ pos = sbd->data;
+ bits = udf_rw32(sbd->num_bits);
+
+ /* shield */
+ /* if (bits > 2000*8) bits = 2000*8; */
+
+ printf("\t\t\t\t");
+ cnt = 0; from = 0; now = 0; bitpos = 0; byte = *pos; state = byte & 1;
+ while (now < bits) {
+ if (bitpos == 0) {
+ byte = *pos++;
+ }
+ bit = byte & 1;
+ if (bit != state) {
+ if (state) {
+ printf("[%08u - %08u]", from, now-1);
+ if (cnt % 4 == 3) printf("\n\t\t\t\t"); else printf(" ");
+ cnt++;
+ }
+ from = now;
+ state = bit;
+ }
+ byte >>= 1;
+ bitpos = (bitpos+1) & 7;
+ now++;
+ }
+ if (state) printf("[%08u - %08u]", from, now);
+ if (bits < udf_rw32(sbd->num_bits)) printf(" .... <trimmed>\n");
+}
+
+
+/* main descriptor `dump' function */
+void udf_dump_descriptor(union dscrptr *dscrpt) {
+ struct desc_tag *tag = &dscrpt->tag;
+ int error;
+
+ if (!dscrpt)
+ return;
+
+ /* check if its a valid descritor */
+ if (udf_rw16(tag->id == 0) && udf_rw16(tag->descriptor_ver) == 0) return;
+
+ udf_dump_desc(tag);
+
+ error = udf_check_tag(dscrpt);
+ if (error) {
+ printf("\tBAD TAG\n");
+ return;
+ }
+ switch (udf_rw16(tag->id)) {
+ case TAGID_SPARING_TABLE :
+ udf_dump_sparing_table(&dscrpt->spt);
+ break;
+ case TAGID_PRI_VOL :
+ udf_dump_pri_vol(&dscrpt->pvd);
+ break;
+ case TAGID_ANCHOR :
+ udf_dump_anchor(&dscrpt->avdp);
+ break;
+ case TAGID_VOL :
+ udf_dump_unimpl(dscrpt, "volume descriptor");
+ break;
+ case TAGID_IMP_VOL :
+ udf_dump_implementation_volume(&dscrpt->ivd);
+ break;
+ case TAGID_PARTITION :
+ udf_dump_part(&dscrpt->pd);
+ break;
+ case TAGID_LOGVOL :
+ udf_dump_log_vol(&dscrpt->lvd);
+ break;
+ case TAGID_UNALLOC_SPACE :
+ udf_dump_unalloc_space(&dscrpt->usd);
+ break;
+ case TAGID_TERM :
+ udf_dump_terminating_desc(dscrpt);
+ break;
+ case TAGID_LOGVOL_INTEGRITY :
+ udf_dump_logvol_integrity(&dscrpt->lvid);
+ break;
+ case TAGID_FSD :
+ udf_dump_fileset_desc(&dscrpt->fsd);
+ break;
+ case TAGID_FID :
+ udf_dump_fileid(&dscrpt->fid);
+ break;
+ case TAGID_ALLOCEXTENT :
+ udf_dump_alloc_extent(&dscrpt->aee, -1);
+ break;
+ case TAGID_INDIRECT_ENTRY :
+ udf_dump_indirect_entry(&dscrpt->inde);
+ break;
+ case TAGID_FENTRY :
+ udf_dump_file_entry(&dscrpt->fe);
+ break;
+ case TAGID_EXTATTR_HDR :
+ udf_dump_extattr_hdr(&dscrpt->eahd, sizeof(struct extattrhdr_desc));
+ break;
+ case TAGID_UNALL_SP_ENTRY :
+ udf_dump_space_entry(&dscrpt->sed);
+ break;
+ case TAGID_SPACE_BITMAP :
+ udf_dump_space_bitmap(&dscrpt->sbd);
+ break;
+ case TAGID_PART_INTEGRETY :
+ udf_dump_unimpl(dscrpt, "partition integrity");
+ break;
+ case TAGID_EXTFENTRY :
+ udf_dump_extfile_entry(&dscrpt->efe);
+ break;
+ default :
+ break;
+ }
+ printf("\n");
+}
+
+
+/* this one is special since the VAT table has no tag but is a file */
+void udf_dump_vat_table(struct udf_part_mapping *udf_part_mapping) {
+ struct charspec chsp;
+ struct udf_vat *vat;
+ uint32_t previous_vat, entry, vat_entries, *vat_pos, version;
+
+ /* prolly OSTA compressed unicode anyway */
+ chsp.type = 0;
+ strcpy((char *) chsp.inf, "OSTA Compressed Unicode");
+
+ vat = udf_part_mapping->vat;
+ printf("\tVAT table: ");
+ printf("%s UDF 2.00 format\n", vat?"post":"pre");
+
+ vat_entries = udf_part_mapping->vat_entries;
+ vat_pos = (uint32_t *) udf_part_mapping->vat_translation;
+ if (vat) {
+ printf("\t\tHeader length %d\n", udf_rw16(vat->header_len));
+ printf("\t\tImplementation use length %d\n", udf_rw16(vat->impl_use_len));
+ udf_dump_id("\t\tLogical volume id ", 128, vat->logvol_id, &chsp);
+ printf("\t\tNumber of files %d\n", udf_rw32(vat->num_files));
+ printf("\t\tNumber of directories %d\n", udf_rw32(vat->num_directories));
+ version = udf_rw16(vat->min_udf_readver);
+ printf("\t\tMinimum readversion UDFv %x\n", version);
+ version = udf_rw16(vat->min_udf_writever);
+ printf("\t\tMinimum writeversion UDFv %x\n", version);
+ version = udf_rw16(vat->max_udf_writever);
+ printf("\t\tMaximum writeversion UDFv %x\n", version);
+ if (udf_rw16(vat->impl_use_len)) printf("\t\t<undumped implementation use area>");
+ previous_vat = udf_rw32(vat->prev_vat);
+ } else {
+ udf_dump_regid("\t\tIdentifier id (can be wrong) ", (struct regid *) (vat_pos+vat_entries), UDF_REGID_NAME);
+ previous_vat = udf_rw32(*(vat_pos + vat_entries + 32/4)); /* definition */
+ }
+ if (previous_vat == 0xffffffff) {
+ printf("\t\tNo previous VAT recorded\n");
+ } else {
+ printf("\t\tPrevious VAT recorded at offset %d\n", previous_vat);
+ }
+
+ printf("\t\tNumber of VAT entries %d\n", vat_entries);
+ printf("\t\tVAT dump :");
+ for (entry=0; entry < vat_entries; entry++) {
+ if ((entry % 4) == 0) printf("\n\t");
+ printf("[0x%08x -> 0x%08x] ", entry, *vat_pos++);
+ }
+ printf("\n");
+}
+
+
+void udf_dump_volumeset_info(struct udf_volumeset *udf_volumeset) {
+ struct udf_pri_vol *primary;
+ struct udf_log_vol *logical;
+ struct udf_partition *udf_partition;
+ struct udf_part_mapping *udf_part_mapping;
+ struct udf_discinfo *disc;
+ char *name;
+ int num_volumes, num_partitions;
+ int subvolume, part_num, track_num;
+
+ num_volumes = 0; /* shut up gcc */
+ if (udf_volumeset->obsolete) return;
+
+ primary = STAILQ_FIRST(&udf_volumeset->primaries);
+ if (primary) {
+ num_volumes = udf_rw16(primary->pri_vol->max_vol_seq);
+ if (udf_volumeset->obsolete) printf("OBSOLETE\n"); /* XXX */
+
+ printf("Volume set ");
+ udf_dump_id(NULL, 32, primary->pri_vol->volset_id, &primary->pri_vol->desc_charset);
+ printf(" (%d volume%s) ", num_volumes, num_volumes>1?"s":"");
+
+ num_partitions = udf_volumeset->max_partnum;
+ printf("with %d partition%s\n", num_partitions, (num_partitions!=1)?"s":"");
+
+ /* better loop trough the partition numbers to display them in a defined order */
+ SLIST_FOREACH(udf_partition, &udf_volumeset->parts, next_partition) {
+ part_num = udf_rw16(udf_partition->partition->part_num);
+ if (udf_partition) {
+ /* there is information */
+ assert(udf_partition->udf_session);
+ assert(udf_partition->udf_session->disc);
+ assert(udf_partition->partition);
+ assert(part_num == udf_rw16(udf_partition->partition->part_num));
+ track_num = udf_partition->udf_session->session_num;
+ disc = udf_partition->udf_session->disc;
+
+ printf("\tPartition number %d at device `%s' session %d from sector %d(+%d) for %u sectors\n",
+ part_num,
+ disc->dev->dev_name,
+ track_num,
+ udf_rw32(udf_partition->partition->start_loc),
+ udf_partition->udf_session->session_offset,
+ udf_rw32(udf_partition->partition->part_len)
+ );
+ } else {
+ printf("\tUnknown partition %d [unknown]\n", part_num);
+ }
+ }
+ }
+
+ STAILQ_FOREACH(primary, &udf_volumeset->primaries, next_primary) {
+ subvolume = udf_rw16(primary->pri_vol->vds_num);
+
+ printf("\tPrimary volume ");
+ udf_dump_id(NULL, 32, primary->pri_vol->vol_id, &primary->pri_vol->desc_charset);
+ printf(" (part %d/%d) ", subvolume, num_volumes);
+
+ printf("created by implementator `%s' ", primary->pri_vol->imp_id.id);
+ if (*primary->pri_vol->app_id.id)
+ printf("by/for application `%s' ",primary->pri_vol->app_id.id);
+ printf("\n");
+
+ SLIST_FOREACH(logical, &primary->log_vols, next_logvol) {
+ name = logical->log_vol->logvol_id;
+ udf_dump_id("\t\tcontains logical volume ", 128, name, &logical->log_vol->desc_charset);
+ if (logical->broken) {
+ printf("\t\t\tBROKEN\n");
+ continue;
+ }
+
+ SLIST_FOREACH(udf_part_mapping, &logical->part_mappings, next_mapping) {
+ printf("\t\t\tmapping %d on %d as ", udf_part_mapping->udf_virt_part_num,
+ udf_part_mapping->udf_phys_part_num);
+ switch (udf_part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_ERROR :
+ printf("bad partition");
+ break;
+ case UDF_PART_MAPPING_PHYSICAL :
+ printf("direct");
+ break;
+ case UDF_PART_MAPPING_VIRTUAL :
+ printf("virtual partition");
+ break;
+ case UDF_PART_MAPPING_SPARABLE :
+ printf("sparable");
+ break;
+ case UDF_PART_MAPPING_META :
+ printf("metadata only");
+ }
+ printf(" recording");
+ if (udf_part_mapping->data_writable) printf(" data");
+ if (udf_part_mapping->metadata_writable) printf(" metadata");
+ if (!udf_part_mapping->data_writable && !udf_part_mapping->metadata_writable) printf(" nothing");
+ printf("\n");
+ }
+ }
+ printf("\n");
+ }
+}
+
+
+void udf_dump_alive_sets(void) {
+ struct udf_volumeset *udf_volumeset;
+
+ printf("UDF volume sets marked alive :\n");
+ SLIST_FOREACH(udf_volumeset, &udf_volumeset_list, next_volumeset) {
+ udf_dump_volumeset_info(udf_volumeset);
+ }
+ printf("\n");
+}
+
+
+/*
+ * extern defined read_logvol_descriptor breaks splitting rules but how
+ * otherwise to provide a detailed description of the file entry udf_node
+ */
+
+#define DUMP_DIRBUFFER_SIZE (16*1024)
+void udf_dump_file_entry_node(struct udf_node *udf_node, char *prefix) {
+ struct long_ad udf_icbptr;
+ struct uio dir_uio;
+ struct iovec dir_iovec;
+ struct dirent *dirent;
+ struct fileid_desc *fid;
+ struct udf_node *entry_node;
+ uint32_t pos, lb_size;
+ uint8_t *buffer;
+ char fullpath[1024]; /* XXX arbitrary length XXX */
+ int isdot, isdotdot, isdir, found, eof;
+ int error;
+
+ if (!udf_node)
+ return;
+
+ /* XXX could pass on dirent XXX */
+ isdir = (udf_node->udf_filetype == UDF_ICB_FILETYPE_DIRECTORY);
+ isdir |= (udf_node->udf_filetype == UDF_ICB_FILETYPE_STREAMDIR);
+ if (isdir) {
+ buffer = malloc(DUMP_DIRBUFFER_SIZE);
+ if (!buffer)
+ return;
+ lb_size = udf_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ assert(fid); /* or just return? */
+
+ /* recurse into this directory */
+ dir_uio.uio_offset = 0; /* begin at start */
+ do {
+ dir_iovec.iov_base = buffer;
+ dir_iovec.iov_len = DUMP_DIRBUFFER_SIZE;
+ dir_uio.uio_resid = DUMP_DIRBUFFER_SIZE;
+ dir_uio.uio_iovcnt = 1;
+ dir_uio.uio_iov = &dir_iovec;
+ dir_uio.uio_rw = UIO_WRITE;
+
+ error = udf_readdir(udf_node, &dir_uio, &eof);
+ if (error) {
+ printf("While reading in dirbuffer for dumping file entry udf_node : %s\n", strerror(error));
+ break;
+ }
+ pos = 0;
+ while (pos < DUMP_DIRBUFFER_SIZE - dir_uio.uio_resid) {
+ dirent = (struct dirent *) (buffer + pos);
+
+ sprintf(fullpath, "%s/%s", prefix, dirent->d_name);
+
+ /* looking for '.' or '..' ? */
+ isdot = (strncmp(dirent->d_name, ".", DIRENT_NAMLEN(dirent)) == 0);
+ isdotdot = (strncmp(dirent->d_name, "..", DIRENT_NAMLEN(dirent)) == 0);
+
+ pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */
+
+ if (isdotdot)
+ continue;
+
+ if (isdot)
+ continue;
+
+ error = udf_lookup_name_in_dir(udf_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found);
+ if (!error) {
+ error = ENOENT;
+ if (found)
+ error = udf_readin_udf_node(udf_node, &udf_icbptr, fid, &entry_node);
+ }
+ if (!error)
+ udf_dump_file_entry_node(entry_node, fullpath);
+ }
+ } while (!eof);
+
+ free(fid);
+ free(buffer);
+ return;
+ }
+ /* leaf udf_node */
+ printf("%s\n", prefix);
+}
+#undef DUMP_DIRBUFFER_SIZE
+
+
+void udf_dump_root_dir(struct udf_mountpoint *mountpoint) {
+ printf("\n\nRoot dir dump\n");
+ if (mountpoint->rootdir_node) udf_dump_file_entry_node(mountpoint->rootdir_node, ":Rootdir");
+
+ printf("\n\nStreamdir dump\n");
+ if (mountpoint->streamdir_node) udf_dump_file_entry_node(mountpoint->streamdir_node, ":Streamdir");
+}
+
+
+/* XXX These should move to form one cd verbose file with cd_discect XXX */
+static char *print_disc_state(int state) {
+ switch (state) {
+ case 0: return "empty disc";
+ case 1: return "incomplete (appendable)";
+ case 2: return "full (not appendable)";
+ case 3: return "random writable";
+ }
+ return "unknown disc state";
+}
+
+
+static char *print_session_state(int state) {
+ switch (state) {
+ case 0 : return "empty";
+ case 1 : return "incomplete";
+ case 2 : return "reserved/damaged";
+ case 3 : return "complete/closed disc";
+ }
+ return "unknown session_state";
+}
+
+
+static char *print_mmc_profile(int profile) {
+ static char scrap[100];
+
+ switch (profile) {
+ case 0x00 : return "Unknown[0] profile";
+ case 0x01 : return "Non removable disc";
+ case 0x02 : return "Removable disc";
+ case 0x03 : return "Magneto Optical with sector erase";
+ case 0x04 : return "Magneto Optical write once";
+ case 0x05 : return "Advance Storage Magneto Optical";
+ case 0x08 : return "CD-ROM";
+ case 0x09 : return "CD-R recordable";
+ case 0x0a : return "CD-RW rewritable";
+ case 0x10 : return "DVD-ROM";
+ case 0x11 : return "DVD-R sequential";
+ case 0x12 : return "DVD-RAM rewritable";
+ case 0x13 : return "DVD-RW restricted overwrite";
+ case 0x14 : return "DVD-RW sequential";
+ case 0x1a : return "DVD+RW rewritable";
+ case 0x1b : return "DVD+R recordable";
+ case 0x20 : return "DDCD readonly";
+ case 0x21 : return "DDCD-R recodable";
+ case 0x22 : return "DDCD-RW rewritable";
+ case 0x2b : return "DVD+R double layer";
+ case 0x40 : return "BD-ROM";
+ case 0x41 : return "BD-R Sequential Recording (SRM)";
+ case 0x42 : return "BD-R Random Recording (RRM)";
+ case 0x43 : return "BD-RE rewritable";
+ }
+ sprintf(scrap, "Reserved profile 0x%02x", profile);
+ return scrap;
+}
+
+
+void udf_dump_discinfo(struct udf_discinfo *disc) {
+ int session;
+
+ printf("Disc info for disc in device %s\n", disc->dev->dev_name);
+ printf("\tMMC profile : %s\n", print_mmc_profile(disc->mmc_profile));
+ printf("\tsequential : %s\n", disc->sequential ?"yes":" no");
+ printf("\trecordable : %s\n", disc->recordable ?"yes":" no");
+ printf("\terasable : %s\n", disc->erasable ?"yes":" no");
+ printf("\tblankable : %s\n", disc->blankable ?"yes":" no");
+ printf("\tformattable : %s\n", disc->formattable ?"yes":" no");
+ printf("\trewritable : %s\n", disc->rewritable ?"yes":" no");
+ printf("\tmount raineer : %s\n", disc->mrw ?"yes":" no");
+ printf("\tpacket writing : %s\n", disc->packet ?"yes":" no");
+ printf("\tstrict overwrite : %s\n", disc->strict_overwrite ?"yes":" no");
+ printf("\tblocking number : %d\n", disc->blockingnr);
+ printf("\tdisc state : %s\n", print_disc_state(disc->disc_state));
+ printf("\tlast session state : %s\n", print_session_state(disc->last_session_state));
+ printf("\tsectorsize : %d\n", disc->sector_size);
+ printf("\tNumber of sessions %d\n", disc->num_sessions);
+ for (session = 0; session < disc->num_sessions; session++) {
+ printf("\tSession %d\n", session);
+ printf("\t\tstart at %u\n", (uint32_t) disc->session_start[session]);
+ printf("\t\tends at %u\n", (uint32_t) disc->session_end[session]);
+ printf("\t\tlength for %u\n", (uint32_t) (disc->session_end[session] - disc->session_start[session]));
+ printf("\t\tnext writable at %u\n", disc->next_writable[session]);
+ printf("\t\tfree blocks %u\n", disc->free_blocks[session]);
+ printf("\t\tpacket size %u\n", disc->packet_size[session]);
+ printf("\n");
+ }
+}
+
+/* end of udf_verbose.c */
+
diff --git a/udfclient.c b/udfclient.c
new file mode 100644
index 0000000..aa7632b
--- /dev/null
+++ b/udfclient.c
@@ -0,0 +1,1781 @@
+/* $NetBSD$ */
+
+/*
+ * File "udfclient.c" is part of the UDFclient toolkit.
+ * File $Id: udfclient.c,v 1.102 2015/08/05 18:26:31 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+#include "udf.h"
+#include "udf_bswap.h"
+
+/* switches */
+
+/* #define DEBUG(a) (a) */
+#define DEBUG(a) if (0) { a; }
+
+
+#ifndef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+/* include timeval to timespec conversion macro's for systems that don't provide them */
+#ifndef TIMEVAL_TO_TIMESPEC
+# define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+ } while (/*CONSTCOND*/0)
+#endif
+#ifndef TIMESPEC_TO_TIMEVAL
+# define TIMESPEC_TO_TIMEVAL(tv, ts) do { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ } while (/*CONSTCOND*/0)
+#endif
+
+
+/* include the dump parts ... in order to get a more sane splitting */
+extern void udf_dump_alive_sets(void);
+
+
+/* globals */
+extern int udf_verbose;
+extern int uscsilib_verbose;
+int read_only;
+
+
+#define MAX_ARGS 100
+
+
+struct curdir {
+ char *name;
+ struct udf_mountpoint *mountpoint; /* foreign */
+ struct udf_node *udf_node; /* foreign */
+ struct hash_entry *udf_mountpoint_entry; /* `current' mountpoint entry */
+} curdir;
+
+
+
+/*
+ * XXX
+ * FTP client; de volumes vooraan zetten in de VFS ; evt. in meerdere subdirs.
+ * general file name format
+ *
+ * /volset:pri:log/udfpath
+ * of
+ * /volset/pri/log/udfpath/
+ *
+ * XXX
+ */
+
+int udfclient_readdir(struct udf_node *udf_node, struct uio *result_uio, int *eof_res) {
+ struct dirent entry;
+ struct udf_mountpoint *mountable;
+
+ assert(result_uio);
+ if (!udf_node) {
+ /* mountables */
+ /* XXX result_uio needs to be long enough to hold all mountables!!!! XXX */
+ SLIST_FOREACH(mountable, &udf_mountables, all_next) {
+ strcpy(entry.d_name, mountable->mount_name);
+ entry.d_type = DT_DIR;
+ uiomove(&entry, sizeof(struct dirent), result_uio);
+ }
+ if (eof_res) *eof_res = 1;
+ return 0;
+ }
+
+ /* intree nodes : pass on to udf_readdir */
+ return udf_readdir(udf_node, result_uio, eof_res);
+}
+
+
+/* VOP_LOOKUP a-like */
+int udfclient_lookup(struct udf_node *dir_node, struct udf_node **resnode, char *name) {
+ struct udf_mountpoint *mountable;
+ struct fileid_desc *fid;
+ struct long_ad udf_icbptr;
+ int lb_size, found, error;
+
+ assert(resnode);
+ assert(name);
+ *resnode = NULL;
+ if (!dir_node) {
+ /* mountables */
+ SLIST_FOREACH(mountable, &udf_mountables, all_next) {
+ if (strcmp(mountable->mount_name, name) == 0) {
+ /* found `root' of a mountable */
+ *resnode = mountable->rootdir_node;
+ return 0;
+ }
+ }
+ return ENOENT;
+ }
+
+ /* intree nodes : pass on to udf_lookup_name_in_dir */
+ lb_size = dir_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ assert(fid);
+
+ error = udf_lookup_name_in_dir(dir_node, name, strlen(name), &udf_icbptr, fid, &found);
+ if (!error) {
+ error = ENOENT;
+ if (found)
+ error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, resnode);
+ }
+
+ free(fid);
+ return error;
+}
+
+
+int udfclient_getattr(struct udf_node *udf_node, struct stat *stat) {
+ int error;
+
+ error = 0;
+ if (udf_node) {
+ error = udf_getattr(udf_node, stat);
+ /* print? */
+ if (error) fprintf(stderr, "Can't stat file\n");
+ } else {
+ /* dummy entry for `root' in VFS */
+ stat->st_mode = 0744 | S_IFDIR;
+ stat->st_size = 0;
+ stat->st_uid = 0;
+ stat->st_gid = 0;
+ }
+
+ return error;
+}
+
+
+/* higher level of lookup; walk tree */
+/* results in a `held'/locked node upto `root' */
+int udfclient_lookup_pathname(struct udf_node *cur_node, struct udf_node **res_node, char *restpath_given) {
+ struct udf_node *sub_node;
+ char *restpath, *next_element, *slashpos, *pathpos;
+ int error;
+
+ assert(restpath_given);
+ restpath = strdup(restpath_given);
+
+ /* start at root */
+ *res_node = NULL;
+ pathpos = restpath;
+ assert(*pathpos == '/');
+ pathpos++; /* strip leading '/' */
+
+ next_element = pathpos;
+ while (next_element && (strlen(next_element) > 0)) {
+ /* determine next slash position */
+ slashpos = strchr(next_element, '/');
+ if (slashpos) *slashpos++ = 0;
+
+ error = udfclient_lookup(cur_node, &sub_node, next_element);
+ if (error) {
+ free(restpath);
+ return error;
+ }
+
+ /* advance */
+ cur_node = sub_node;
+ next_element = slashpos;
+ }
+ /* we are at the end; return result */
+ *res_node = cur_node;
+ free(restpath);
+ return 0;
+}
+
+
+char *udfclient_realpath(char *cur_path, char *relpath, char **leaf) {
+ char *resultpath, *here, *pos;
+
+ resultpath = calloc(1, sizeof(cur_path) + sizeof(relpath) +1024);
+ assert(resultpath);
+
+ strcpy(resultpath, "/");
+ strcat(resultpath, cur_path);
+ strcat(resultpath, "/");
+
+ /* check if we are going back to `root' */
+ if (relpath && *relpath == '/') {
+ strcpy(resultpath, "");
+ }
+ strcat(resultpath, relpath);
+ /* now clean up the resulting string by removing double slashes */
+ here = resultpath;
+ while (*here) {
+ pos = here; while (strncmp(pos, "//", 2) == 0) pos++;
+ if (pos != here) strcpy(here, pos);
+ here++;
+ }
+
+ /* remove '.' and '..' sequences */
+ here = resultpath;
+ while (*here) {
+ /* printf("transformed to %s\n", resultpath); */
+ /* check for internal /./ and trailing /. */
+ if (strncmp(here, "/./", 3) == 0) {
+ strcpy(here+1, here + 3);
+ continue;
+ }
+ if (strcmp(here, "/.")==0) {
+ strcpy(here+1, here + 2);
+ continue;
+ }
+ if (strncmp(here, "/../", 4) == 0) {
+ strcpy(here+1, here + 4);
+ /* go for the parent */
+ pos = here-1; while (*pos && *pos != '/') pos--; pos++;
+ strcpy(pos, here+1);
+ here = pos;
+ continue;
+ }
+ if (strcmp(here, "/..")==0) {
+ strcpy(here+1, here + 3);
+ /* go for the parent */
+ pos = here-1; while (*pos && *pos != '/') pos--; pos++;
+ strcpy(pos, here+1);
+ here = pos;
+ continue;
+ }
+ here++;
+ }
+ if (leaf) {
+ /* find the leaf name */
+ here = resultpath;
+ while (*here) {
+ if (strncmp(here, "/", 1) == 0)
+ *leaf = here+1;
+ here++;
+ }
+ }
+
+ return resultpath;
+}
+
+
+static void print_dir_entry(struct udf_node *udf_node, char *name) {
+ struct stat stat;
+ uint64_t size;
+ int mode, this_mode, uid, gid;
+ int error;
+
+ error = udfclient_getattr(udf_node, &stat);
+ if (error) return;
+
+ size = stat.st_size;
+ mode = stat.st_mode;
+ uid = stat.st_uid;
+ gid = stat.st_gid;
+
+ if (mode & S_IFDIR) printf("d"); else printf("-");
+ mode = mode & 511;
+
+ this_mode = (mode >> 6) & 7;
+ printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
+ this_mode = (mode >> 3) & 7;
+ printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
+ this_mode = mode & 7;
+ printf("%c%c%c", "----rrrr"[this_mode & 4], "--www"[this_mode & 2], "-x"[this_mode & 1]);
+
+ printf(" %5d %5d %10"PRIu64" %s\n", gid, uid, size, name);
+}
+
+
+#define LS_SUBTREE_DIR_BUFFER_SIZE (16*1024)
+void udfclient_ls(int args, char *arg1) {
+ struct udf_node *udf_node, *entry_node;
+ uint8_t *buffer;
+ struct uio dir_uio;
+ struct iovec dir_uiovec;
+ struct dirent *dirent;
+ struct stat stat;
+ uint32_t pos;
+ int eof;
+ char *node_name, *leaf_name;
+ int error;
+
+ if (args > 1) {
+ printf("Syntax: ls [file | dir]\n");
+ return;
+ }
+ if (args == 0) arg1 = "";
+
+ node_name = udfclient_realpath(curdir.name, arg1, &leaf_name);
+
+ error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
+ if (error) {
+ fprintf(stderr, "%s : %s\n", arg1, strerror(error));
+ free(node_name);
+ return;
+ }
+
+ error = udfclient_getattr(udf_node, &stat);
+ if (stat.st_mode & S_IFDIR) {
+ printf("Directory listing of %s\n", udf_node ? leaf_name : "/");
+
+ /* start at the start of the directory */
+ dir_uio.uio_offset = 0;
+ dir_uio.uio_iov = &dir_uiovec;
+ dir_uio.uio_iovcnt = 1;
+ buffer = calloc(1, LS_SUBTREE_DIR_BUFFER_SIZE);
+ if (!buffer) return;
+
+ do {
+ dir_uiovec.iov_base = buffer;
+ dir_uiovec.iov_len = LS_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_resid = LS_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_rw = UIO_WRITE;
+
+ error = udfclient_readdir(udf_node, &dir_uio, &eof);
+ if (error) {
+ fprintf(stderr, "error during readdir: %s\n", strerror(error));
+ break;
+ }
+ pos = 0;
+ while (pos < LS_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
+ dirent = (struct dirent *) (buffer + pos);
+ error = udfclient_lookup(udf_node, &entry_node, dirent->d_name);
+ print_dir_entry(entry_node, dirent->d_name);
+
+ pos += sizeof(struct dirent);
+ }
+ } while (!eof);
+ free(buffer);
+ } else {
+ print_dir_entry(udf_node, leaf_name);
+ }
+ free(node_name);
+}
+#undef LS_SUBTREE_DIR_BUFFER_SIZE
+
+
+void udfclient_pwd(int args) {
+ char pwd[1024];
+
+ if (args) {
+ printf("Syntax: pwd\n");
+ return;
+ }
+ getcwd(pwd, 1024);
+ printf("UDF working directory is %s\n", curdir.name);
+ printf("Current FS working directory %s\n", pwd);
+}
+
+
+static void udfclient_print_free_amount(char *prefix, uint64_t value, uint64_t max_value) {
+ printf("%s %10"PRIu64" Kb (%3"PRIu64" %%) (%8.2f Mb) (%5.2f Gb)\n",
+ prefix, value/1024, (100*value)/max_value, (double) value/(1024*1024), (double) value/(1024*1024*1024));
+}
+
+
+void udfclient_free(int args) {
+ struct udf_part_mapping *part_mapping;
+ struct udf_partition *udf_partition;
+ struct udf_log_vol *udf_log_vol;
+ struct logvol_desc *lvd;
+ uint64_t part_size, unalloc_space, freed_space;
+ uint64_t total_space, free_space, await_alloc_space;
+ uint32_t sector_size, lb_size;
+ int part_num;
+
+ if (args) {
+ printf("Syntax: free\n");
+ return;
+ }
+
+ if (!curdir.udf_node || !(udf_log_vol = curdir.udf_node->udf_log_vol)) {
+ printf("Can only report free space in UDF mountpoints\n");
+ return;
+ }
+
+ lb_size = udf_log_vol->lb_size;
+ sector_size = udf_log_vol->sector_size;
+
+ lvd = udf_log_vol->log_vol;
+ udf_dump_id("Logical volume ", 128, lvd->logvol_id, &lvd->desc_charset);
+
+ total_space = udf_log_vol->total_space;
+ free_space = udf_log_vol->free_space;
+ await_alloc_space = udf_log_vol->await_alloc_space;
+
+ SLIST_FOREACH(part_mapping, &udf_log_vol->part_mappings, next_mapping) {
+ part_num = part_mapping->udf_virt_part_num;
+ udf_logvol_vpart_to_partition(udf_log_vol, part_num, NULL, &udf_partition);
+ assert(udf_partition);
+
+ unalloc_space = udf_partition->free_unalloc_space;
+ freed_space = udf_partition->free_freed_space;
+ part_size = udf_partition->part_length;
+
+ switch (part_mapping->udf_part_mapping_type) {
+ case UDF_PART_MAPPING_PHYSICAL :
+ printf("\tphysical partition %d\n", part_num);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
+ break;
+ case UDF_PART_MAPPING_VIRTUAL :
+ printf("\tvirtual partition mapping %d\n", part_num);
+ printf("\t\tnot applicable\n");
+ break;
+ case UDF_PART_MAPPING_SPARABLE :
+ printf("\tsparable partition %d\n", part_num);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) size\n", part_size/1024, part_size / lb_size);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
+ break;
+ case UDF_PART_MAPPING_META :
+ printf("\tmetadata 'partition' %d\n", part_num);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) unallocated\n", unalloc_space/1024, unalloc_space / lb_size);
+ printf("\t\t%8"PRIu64" K (%"PRIu64" pages) freed\n", freed_space/1024, freed_space / lb_size);
+ break;
+ case UDF_PART_MAPPING_ERROR :
+ printf("\terror partiton %d\n", part_num);
+ break;
+ default:
+ break;
+ }
+ }
+ printf("\n");
+ udfclient_print_free_amount("\tConfirmed free space ", free_space, total_space);
+ udfclient_print_free_amount("\tAwaiting allocation ", await_alloc_space, total_space);
+ udfclient_print_free_amount("\tEstimated free space ", free_space - await_alloc_space, total_space);
+ udfclient_print_free_amount("\tEstimated total used ", total_space - free_space + await_alloc_space, total_space);
+ printf("\n");
+ udfclient_print_free_amount("\tTotal size ", total_space, total_space);
+}
+
+
+void udfclient_cd(int args, char *arg1) {
+ struct udf_node *udf_node;
+ struct stat stat;
+ char *node_name, *new_curdir_name;
+ int error;
+
+ if (args > 1) {
+ printf("Syntax: cd [dir]\n");
+ return;
+ }
+
+ new_curdir_name = udfclient_realpath(curdir.name, arg1, NULL);
+
+ node_name = strdup(new_curdir_name); /* working copy */
+ error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
+ if (error) {
+ fprintf(stderr, "%s : %s\n", arg1, strerror(error));
+ free(new_curdir_name);
+ free(node_name);
+ return;
+ }
+
+ error = udfclient_getattr(udf_node, &stat);
+ if (stat.st_mode & S_IFDIR) {
+ free(curdir.name);
+ curdir.name = new_curdir_name;
+ curdir.udf_node = udf_node;
+ free(node_name);
+
+ udfclient_pwd(0);
+ } else {
+ fprintf(stderr, "%s is not a directory\n", arg1);
+ free(new_curdir_name);
+ free(node_name);
+ }
+}
+
+
+void udfclient_lcd(int args, char *arg1) {
+ char pwd[1024];
+
+ if (args > 1) {
+ printf("Syntax: lcd [dir]\n");
+ return;
+ }
+
+ if (strcmp(arg1, "" )==0) arg1 = getenv("HOME");
+ if (strcmp(arg1, "~")==0) arg1 = getenv("HOME");
+
+ if (chdir(arg1)) {
+ fprintf(stderr, "While trying to go to %s :", arg1);
+ perror("");
+ }
+ getcwd(pwd, 1024);
+ printf("Changing local directory to %s\n", pwd);
+}
+
+
+void udfclient_lls(int args) {
+ if (args) {
+ printf("Syntax: lls\n");
+ return;
+ }
+ if (system("/bin/ls")) {
+ perror("While listing current directory\n");
+ }
+}
+
+
+uint64_t getmtime(void) {
+ struct timeval tp;
+
+ gettimeofday(&tp, NULL);
+ return 1000000*tp.tv_sec + tp.tv_usec;
+}
+
+
+
+int udfclient_get_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) {
+ struct uio file_uio;
+ struct iovec file_iov;
+ struct stat stat;
+ struct timeval times[2];
+ uint64_t file_length;
+ uint64_t start, now, then, eta;
+ uint64_t cur_speed, avg_speed, data_transfered;
+ uint64_t file_block_size, file_transfer_size;
+ uint8_t *file_block;
+ char cur_txt[32], avg_txt[32], eta_txt[32];
+ int fileh, len;
+ int notok, error;
+
+ assert(udf_node);
+ assert(fullsrcname);
+ assert(strlen(fullsrcname) >= 1);
+
+ error = 0;
+
+ /* terminal directory node? */
+ error = udfclient_getattr(udf_node, &stat);
+ if (stat.st_mode & S_IFDIR) {
+ len = strlen(fulldstname);
+ if (strcmp(fulldstname + len -2, "/.") == 0)
+ fulldstname[len-2] = 0;
+ if (strcmp(fulldstname + len -3, "/..") == 0)
+ return 0;
+
+ error = mkdir(fulldstname, (udf_node->stat.st_mode) & 07777);
+ if (!error) {
+ /* set owner attribute and times; access permissions allready set on creation.*/
+ notok = chown(fulldstname, stat.st_uid, stat.st_gid);
+ if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS))
+ fprintf(stderr, "failed to set owner of directory, ignoring\n");
+
+ TIMESPEC_TO_TIMEVAL(&times[0], &stat.st_atimespec); /* access time */
+ TIMESPEC_TO_TIMEVAL(&times[1], &stat.st_mtimespec); /* modification time */
+ notok = utimes(fulldstname, times);
+ if (notok)
+ fprintf(stderr, "failed to set times on directory, ignoring\n");
+ }
+ if (error)
+ fprintf(stderr, "While creating directory `%s' : %s\n", fulldstname, strerror(errno));
+
+ return 0;
+ }
+
+ /* terminal file node; setting mode correctly */
+ fileh = open(fulldstname, O_WRONLY | O_CREAT | O_TRUNC, udf_node->stat.st_mode);
+ if (fileh >= 0) {
+ file_length = udf_node->stat.st_size;
+ file_block_size = 256*1024; /* block read in length */
+ file_block = malloc(file_block_size);
+ if (!file_block) {
+ printf("Out of memory claiming file buffer\n");
+ return ENOMEM;
+ }
+
+ /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */
+ bzero(&file_uio, sizeof(struct uio));
+ file_uio.uio_rw = UIO_WRITE; /* WRITE into this space */
+ file_uio.uio_iovcnt = 1;
+ file_uio.uio_iov = &file_iov;
+
+ start = getmtime();
+ then = now = start;
+ eta = data_transfered = 0;
+ strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
+
+ file_uio.uio_offset = 0; /* begin at the start */
+ do {
+ /* fill in IO vector space; reuse blob file_block over and over */
+ file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset);
+ file_uio.uio_resid = file_transfer_size;
+ file_uio.uio_iov->iov_base = file_block;
+ file_uio.uio_iov->iov_len = file_block_size;
+
+ error = udf_read_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio);
+ if (error) {
+ fprintf(stderr, "While retrieving file block : %s\n", strerror(error));
+ printf("\n\n\n"); /* XXX */
+ break;
+ }
+
+ write(fileh, file_block, file_transfer_size);
+
+ if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) {
+ if (strlen(fulldstname) < 45) {
+ printf("\r%-45s ", fulldstname);
+ } else {
+ printf("\r...%-42s ", fulldstname + strlen(fulldstname)-42);
+ }
+ printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length);
+ if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length));
+
+ then = now;
+ now = getmtime();
+ cur_speed = 0;
+ avg_speed = 0;
+ if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start);
+ if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then);
+ if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed;
+ data_transfered = file_uio.uio_offset;
+
+ strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
+ if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1000);
+ if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1000);
+ if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60);
+
+ printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt);
+ fflush(stdout);
+ }
+ } while ((uint64_t) file_uio.uio_offset < file_length);
+ printf(" finished\n");
+ free(file_block);
+
+ /* set owner attribute and times; access permissions allready set on creation.*/
+ notok = fchown(fileh, stat.st_uid, stat.st_gid);
+ if (notok && (udf_verbose > UDF_VERBLEV_ACTIONS))
+ fprintf(stderr, "failed to set owner of file, ignoring\n");
+
+ TIMESPEC_TO_TIMEVAL(&times[0], &stat.st_atimespec); /* access time */
+ TIMESPEC_TO_TIMEVAL(&times[1], &stat.st_mtimespec); /* modification time */
+ notok = futimes(fileh, times);
+ if (notok)
+ fprintf(stderr, "failed to set times on directory, ignoring\n");
+
+ close(fileh);
+ } else {
+ printf("Help! can't open file %s for output\n", fulldstname);
+ }
+
+ return error;
+}
+
+
+#define GET_SUBTREE_DIR_BUFFER_SIZE (16*1024)
+void udfclient_get_subtree(struct udf_node *udf_node, char *srcprefix, char *dstprefix, int recurse, uint64_t *total_size) {
+ struct uio dir_uio;
+ struct iovec dir_iovec;
+ uint8_t *buffer;
+ uint32_t pos;
+ char fullsrcpath[1024], fulldstpath[1024]; /* XXX arbitrary length XXX */
+ struct dirent *dirent;
+ struct stat stat;
+ struct udf_node *entry_node;
+ struct fileid_desc *fid;
+ struct long_ad udf_icbptr;
+ int lb_size, eof;
+ int found, isdot, isdotdot, error;
+
+ if (!udf_node)
+ return;
+
+ udf_node->hold++;
+ error = udfclient_getattr(udf_node, &stat);
+ if ((stat.st_mode & S_IFDIR) && recurse) {
+ buffer = malloc(GET_SUBTREE_DIR_BUFFER_SIZE);
+ if (!buffer) {
+ udf_node->hold--;
+ return;
+ }
+
+ lb_size = udf_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ assert(fid);
+
+ /* recurse into this directory */
+ dir_uio.uio_offset = 0; /* begin at start */
+ do {
+ dir_iovec.iov_base = buffer;
+ dir_iovec.iov_len = GET_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_resid = GET_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_iovcnt = 1;
+ dir_uio.uio_iov = &dir_iovec;
+ dir_uio.uio_rw = UIO_WRITE;
+
+ error = udf_readdir(udf_node, &dir_uio, &eof);
+ pos = 0;
+ while (pos < GET_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
+ dirent = (struct dirent *) (buffer + pos);
+
+ sprintf(fullsrcpath, "%s/%s", srcprefix, dirent->d_name);
+ sprintf(fulldstpath, "%s/%s", dstprefix, dirent->d_name);
+
+ /* looking for '.' or '..' ? */
+ isdot = (strcmp(dirent->d_name, "." ) == 0);
+ isdotdot = (strcmp(dirent->d_name, "..") == 0);
+
+ pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */
+
+ if (isdotdot)
+ continue;
+
+ if (isdot) {
+ /* hack */
+ udfclient_get_subtree(udf_node, fullsrcpath, fulldstpath, 0, total_size);
+ continue;
+ }
+
+ error = udf_lookup_name_in_dir(udf_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found);
+ if (!error) {
+ error = ENOENT;
+ if (found)
+ error = udf_readin_udf_node(udf_node, &udf_icbptr, fid, &entry_node);
+ }
+
+ if (!error)
+ udfclient_get_subtree(entry_node, fullsrcpath, fulldstpath, 1, total_size);
+
+ }
+ } while (!eof);
+
+ udf_node->hold--;
+ free(buffer);
+ free(fid);
+ return;
+ }
+
+ /* leaf node : prefix is complete name but with `/' prefix */
+ if (*srcprefix == '/') srcprefix++;
+ error = udfclient_get_file(udf_node, srcprefix, dstprefix);
+ udf_node->hold--;
+ if (!error)
+ *total_size += udf_node->stat.st_size;
+}
+#undef GET_SUBTREE_DIR_BUFFER_SIZE
+
+
+void udfclient_get(int args, char *arg1, char *arg2) {
+ struct udf_node *udf_node;
+ char *source_name, *target_name;
+ uint64_t start, now, totalsize, avg_speed;
+ int error;
+
+ if (args > 2) {
+ printf("Syntax: get remote [local]\n");
+ return;
+ }
+
+ source_name = arg1;
+ target_name = arg1;
+ if (args == 2)
+ target_name = arg2;
+
+ /* source name gets substituted */
+ source_name = udfclient_realpath(curdir.name, source_name, NULL);
+ DEBUG(printf("Attempting to retrieve %s\n", source_name));
+
+ error = udfclient_lookup_pathname(NULL, &udf_node, source_name);
+ if (error) {
+ fprintf(stderr, "%s : %s\n", arg1, strerror(error));
+ free(source_name);
+ return;
+ }
+
+ /* get the file/dir tree */
+ totalsize = 0;
+ start = getmtime();
+ udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize);
+ now = getmtime();
+ if (now-start > 0) {
+ avg_speed = (1000000 * totalsize) / (now-start);
+ printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
+ } else {
+ printf("Transfered %d kb\n", (uint32_t)(totalsize/1024));
+ }
+
+ /* bugalert: not releasing target_name for its not substituted */
+ free(source_name);
+}
+
+
+
+void udfclient_mget(int args, char *argv[]) {
+ struct udf_node *udf_node;
+ uint64_t start, now, totalsize, avg_speed;
+ char *node_name, *source_name, *target_name;
+ int arg, error;
+
+ if (args == 0) {
+ printf("Syntax: mget (file | dir)*\n");
+ return;
+ }
+
+ /* retrieve the series of file/dir trees and measure time/seed */
+ totalsize = 0;
+ start = getmtime();
+
+ /* process all args */
+ arg = 0;
+ node_name = NULL;
+ while (args) {
+ source_name = target_name = argv[arg];
+
+ node_name = udfclient_realpath(curdir.name, source_name, NULL);
+ DEBUG(printf("Attempting to retrieve %s\n", node_name));
+
+ error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
+ printf("%d: mget trying %s\n", error, node_name);
+ if (!error) {
+ udfclient_get_subtree(udf_node, source_name, target_name, 1, &totalsize);
+ }
+
+ if (node_name) {
+ free(node_name);
+ node_name = NULL;
+ }
+
+ if (error) break; /* TODO continuation flag? */
+
+ /* advance */
+ arg++;
+ args--;
+ }
+
+ now = getmtime();
+ if (now-start > 0) {
+ avg_speed = (1000000 * totalsize) / (now-start);
+ printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
+ } else {
+ printf("Transfered %d kb\n", (uint32_t) (totalsize/1024));
+ }
+
+ if (node_name)
+ free(node_name);
+}
+
+
+int udfclient_put_file(struct udf_node *udf_node, char *fullsrcname, char *fulldstname) {
+ struct uio file_uio;
+ struct iovec file_iov;
+ uint64_t file_length;
+ uint64_t start, now, then, eta;
+ uint64_t cur_speed, avg_speed, data_transfered;
+ uint64_t file_block_size, file_transfer_size;
+ uint8_t *file_block;
+ char cur_txt[32], avg_txt[32], eta_txt[32];
+ int fileh;
+ int error, printed;
+
+ assert(udf_node);
+ assert(fullsrcname);
+
+ DEBUG(printf("Attempting to write %s\n", fullsrcname));
+
+ fileh = open(fullsrcname, O_RDONLY, 0666);
+ if (fileh == -1) {
+ fprintf(stderr, "Can't open local file %s for reading: %s\n", fullsrcname, strerror(errno));
+ return ENOENT;
+ }
+
+ /* get file length */
+ file_length = lseek(fileh, 0, SEEK_END);
+ lseek(fileh, 0, SEEK_SET);
+
+ /* check if file will fit; give it a bit of slack space until the space issue is found and fixed */
+ if (udf_node->udf_log_vol->free_space < file_length + udf_node->udf_log_vol->await_alloc_space + UDF_MINFREE_LOGVOL) {
+ return ENOSPC;
+ }
+
+ /* allocate file block to transfer file with */
+ file_block_size = 128*1024;
+ file_block = malloc(file_block_size);
+ if (!file_block) {
+ fprintf(stderr, "Out of memory claiming file buffer\n");
+ return ENOMEM;
+ }
+
+ /* move to uio_newuio(struct uio *uio) with fixed length uio_iovcnt? */
+ bzero(&file_uio, sizeof(struct uio));
+ file_uio.uio_rw = UIO_READ; /* READ from this space */
+ file_uio.uio_iovcnt = 1;
+ file_uio.uio_iov = &file_iov;
+
+/* ------------ */
+ start = getmtime();
+ then = now = start;
+ eta = data_transfered = 0;
+ printed = 0;
+ strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
+/* ------------ */
+
+ error = 0;
+ error = udf_truncate_node(udf_node, 0);
+ while (!error && ((uint64_t) file_uio.uio_offset < file_length)) {
+ file_transfer_size = MIN(file_block_size, file_length - file_uio.uio_offset);
+
+ error = read(fileh, file_block, file_transfer_size);
+ if (error<0) {
+ fprintf(stderr, "While reading in file block for writing : %s\n", strerror(errno));
+ error = errno;
+ break;
+ }
+
+ file_uio.uio_resid = file_transfer_size;
+ file_uio.uio_iov->iov_base = file_block;
+ file_uio.uio_iov->iov_len = file_block_size;
+
+ error = udf_write_file_part_uio(udf_node, fullsrcname, UDF_C_USERDATA, &file_uio);
+ if (error) {
+ fprintf(stderr, "\nError while writing file : %s\n", strerror(error));
+ break;
+ }
+
+/* ------------ */
+ if ((getmtime() - now > 1000000) || ((uint64_t) file_uio.uio_offset >= file_length)) {
+ printed = 1;
+ if (strlen(fulldstname) < 45) {
+ printf("\r%-45s ", fulldstname);
+ } else {
+ printf("\r...%-42s ", fulldstname+strlen(fulldstname)-42);
+ }
+ printf("%10"PRIu64" / %10"PRIu64" bytes ", (uint64_t) file_uio.uio_offset, (uint64_t) file_length);
+ if (file_length) printf("(%3d%%) ", (int) (100.0*(float) file_uio.uio_offset / file_length));
+
+ then = now;
+ now = getmtime();
+ cur_speed = 0;
+ avg_speed = 0;
+ if (now-start > 0) avg_speed = (1000000 * file_uio.uio_offset) / (now-start);
+ if (now-then > 0) cur_speed = (1000000 * (file_uio.uio_offset - data_transfered)) / (now-then);
+ if (avg_speed > 0) eta = (file_length - file_uio.uio_offset) / avg_speed;
+ data_transfered = file_uio.uio_offset;
+
+ strcpy(avg_txt, "---"); strcpy(cur_txt, "---"); strcpy(eta_txt, "---");
+ if (avg_speed > 0) sprintf(avg_txt, "%d", (int32_t) avg_speed/1024);
+ if (cur_speed > 0) sprintf(cur_txt, "%d", (int32_t) cur_speed/1024);
+ if (eta > 0) sprintf(eta_txt, "%02d:%02d:%02d", (int) (eta/3600), (int) (eta/60) % 60, (int) eta % 60);
+ printf("%6s KB/s (%6s KB/s) ETA %s", avg_txt, cur_txt, eta_txt);
+ fflush(stdout);
+ }
+/* ------------ */
+ }
+ if (!error && printed) printf(" finished\n");
+
+ close(fileh);
+ free(file_block);
+
+ return error;
+}
+
+
+int udfclient_put_subtree(struct udf_node *parent_node, char *srcprefix, char *srcname, char *dstprefix, char *dstname, uint64_t *totalsize) {
+ struct udf_node *file_node, *dir_node;
+ struct dirent *dirent;
+ struct stat stat;
+ DIR *dir;
+ char fullsrcpath[1024], fulldstpath[1024];
+ int error;
+
+ sprintf(fullsrcpath, "%s/%s", srcprefix, srcname);
+ sprintf(fulldstpath, "%s/%s", dstprefix, dstname);
+
+ /* stat source file */
+ bzero(&stat, sizeof(struct stat));
+ error = lstat(fullsrcpath, &stat);
+ if (error) {
+ error = errno; /* lstat symantics; returns -1 on error */
+ fprintf(stderr, "Can't stat file/dir \"%s\"! : %s\n", fullsrcpath, strerror(error));
+ return error;
+ }
+
+ /* test if `srcname' refers to a directory */
+ dir = opendir(fullsrcpath);
+ if (dir) {
+ error = udfclient_lookup(parent_node, &dir_node, dstname);
+ if (error) {
+ DEBUG(printf("Create dir %s on UDF\n", fulldstpath));
+ error = udf_create_directory(parent_node, dstname, &stat, &dir_node);
+ if (error) {
+ closedir(dir);
+ fprintf(stderr, "UDF: couldn't create new directory %s : %s\n", dstname, strerror(error));
+ return error;
+ }
+ }
+
+ dir_node->hold++;
+ dirent = readdir(dir);
+ while (dirent) {
+ if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, "..")) {
+ /* skip `.' and ',,' */
+ error = udfclient_put_subtree(dir_node, fullsrcpath, dirent->d_name, fulldstpath, dirent->d_name, totalsize);
+ if (error) break;
+ }
+ dirent = readdir(dir);
+ }
+ closedir(dir);
+ dir_node->hold--;
+ return error;
+ }
+
+ /* leaf node : prefix is complete name but with `/' prefix */
+ DEBUG(printf("Put leaf: %s\n", fulldstpath));
+
+ error = udfclient_lookup(parent_node, &file_node, dstname);
+ if (!file_node) {
+ error = udf_create_file(parent_node, dstname, &stat, &file_node);
+ if (error) {
+ fprintf(stderr, "UDF: couldn't add new file entry in directory %s for %s : %s\n", dstprefix, dstname, strerror(error));
+ return error;
+ }
+ }
+ file_node->hold++;
+ error = udfclient_put_file(file_node, fullsrcpath, fulldstpath);
+ file_node->hold--;
+
+ if (error) fprintf(stderr, "UDF: Couldn't write file %s : %s\n", fulldstpath, strerror(error));
+ if (error) udf_remove_file(parent_node, file_node, dstname);
+
+ if (!error) *totalsize += file_node->stat.st_size;
+
+ return error;
+}
+
+
+void udfclient_put(int args, char *arg1, char *arg2) {
+ struct udf_node *curdir_node;
+ uint64_t start, now, totalsize, avg_speed;
+ char *source_name, *target_name;
+ int error;
+
+ if (args > 2) {
+ printf("Syntax: put source [destination]\n");
+ return;
+ }
+
+ if (read_only) {
+ printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n");
+ return;
+ }
+
+ error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name);
+ if (error) {
+ printf("Current directory not found?\n");
+ return;
+ }
+ DEBUG(printf("Attempting to copy %s\n", arg1));
+
+
+ /* determine source and destination entities */
+ source_name = arg1;
+ target_name = arg1;
+ if (args == 2)
+ target_name = arg2;
+
+ /* writeout file/dir tree and measure the time/speed */
+ totalsize = 0;
+ start = getmtime();
+ error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize);
+ now = getmtime();
+ if (now-start > 0) {
+ avg_speed = (1000000 * totalsize) / (now-start);
+ printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t) (totalsize/1024), (uint32_t) (avg_speed/1024));
+ } else {
+ printf("Transfered %d kb\n", (uint32_t) (totalsize/1024));
+ }
+}
+
+
+/* args start at position 0 of argv */
+void udfclient_mput(int args, char **argv) {
+ struct udf_node *curdir_node;
+ uint64_t start, now, totalsize, avg_speed;
+ char *source_name, *target_name;
+ int arg, error;
+
+ if (args == 0) {
+ printf("Syntax: mput (file | dir)*\n");
+ return;
+ }
+
+ if (read_only) {
+ printf("Modifying this filingsystem is prevented; use -W flag to enable writing on your own risk!\n");
+ return;
+ }
+
+ error = udfclient_lookup_pathname(NULL, &curdir_node, curdir.name);
+ if (error) {
+ printf("Current directory not found?\n");
+ return;
+ }
+
+ /* writeout file/dir trees and measure the time/speed */
+ totalsize = 0;
+ start = getmtime();
+
+ /* process all args */
+ arg = 0;
+ while (args) {
+ source_name = target_name = argv[arg];
+ error = udfclient_put_subtree(curdir_node, ".", source_name, ".", target_name, &totalsize);
+ if (error) {
+ fprintf(stderr, "While writing file %s : %s\n", source_name, strerror(error));
+ break; /* TODO continuation flag? */
+ }
+
+ /* advance */
+ arg++;
+ args--;
+ }
+
+ now = getmtime();
+ if (now-start > 0) {
+ avg_speed = (1000000 * totalsize) / (now-start);
+ printf("A total of %d kb transfered at an overal average of %d kb/sec\n", (uint32_t)(totalsize/1024), (uint32_t)(avg_speed/1024));
+ } else {
+ printf("Transfered %d kb\n", (uint32_t)(totalsize/1024));
+ }
+
+}
+
+
+
+void udfclient_trunc(int args, char *arg1, char *arg2) {
+ struct udf_node *udf_node;
+ char *node_name;
+ uint64_t length;
+ int error;
+
+ if (args != 2) {
+ printf("Syntax: trunc file length\n");
+ return;
+ }
+ length = strtoll(arg2, NULL, 10);
+
+ node_name = udfclient_realpath(curdir.name, arg1, NULL);
+ error = udfclient_lookup_pathname(NULL, &udf_node, node_name);
+ if (error || !udf_node) {
+ printf("Can only truncate existing file!\n");
+ free(node_name);
+ return;
+ }
+
+ udf_truncate_node(udf_node, length);
+
+ free(node_name);
+}
+
+
+void udfclient_sync(void) {
+ struct udf_discinfo *udf_disc;
+
+ SLIST_FOREACH(udf_disc, &udf_discs_list, next_disc) {
+ udf_sync_disc(udf_disc);
+ }
+}
+
+
+#define RM_SUBTREE_DIR_BUFFER_SIZE (32*1024)
+int udfclient_rm_subtree(struct udf_node *parent_node, struct udf_node *dir_node, char *name, char *full_parent_name) {
+ struct uio dir_uio;
+ struct iovec dir_iovec;
+ uint8_t *buffer;
+ uint32_t pos;
+ char *fullpath;
+ struct dirent *dirent;
+ struct stat stat;
+ struct udf_node *entry_node;
+ struct fileid_desc *fid;
+ struct long_ad udf_icbptr;
+ int lb_size, eof, found, isdot, isdotdot;
+ int error;
+
+ if (!dir_node)
+ return ENOENT;
+
+ error = udfclient_getattr(dir_node, &stat);
+ if (stat.st_mode & S_IFDIR) {
+ buffer = malloc(RM_SUBTREE_DIR_BUFFER_SIZE);
+ if (!buffer)
+ return ENOSPC;
+ lb_size = dir_node->udf_log_vol->lb_size;
+ fid = malloc(lb_size);
+ if (!fid) {
+ free(buffer);
+ return ENOSPC;
+ }
+
+ /* recurse into this directory */
+ dir_uio.uio_offset = 0; /* begin at start */
+ do {
+ dir_iovec.iov_base = buffer;
+ dir_iovec.iov_len = RM_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_resid = RM_SUBTREE_DIR_BUFFER_SIZE;
+ dir_uio.uio_iovcnt = 1;
+ dir_uio.uio_iov = &dir_iovec;
+ dir_uio.uio_rw = UIO_WRITE;
+
+ error = udf_readdir(dir_node, &dir_uio, &eof);
+ pos = 0;
+ while (pos < RM_SUBTREE_DIR_BUFFER_SIZE - dir_uio.uio_resid) {
+ dirent = (struct dirent *) (buffer + pos);
+ pos += sizeof(struct dirent); /* XXX variable size dirents possible XXX */
+
+ /* looking for '.' or '..' ? */
+ isdot = (strcmp(dirent->d_name, "." ) == 0);
+ isdotdot = (strcmp(dirent->d_name, "..") == 0);
+
+ if (isdot || isdotdot)
+ continue;
+
+ error = udf_lookup_name_in_dir(dir_node, dirent->d_name, DIRENT_NAMLEN(dirent), &udf_icbptr, fid, &found);
+ if (!error) {
+ error = ENOENT;
+ if (found)
+ error = udf_readin_udf_node(dir_node, &udf_icbptr, fid, &entry_node);
+ }
+ if (error)
+ break;
+
+ error = udfclient_getattr(entry_node, &stat);
+ if (error)
+ break;
+
+ /* check if the direntry is a directory or a file */
+ if (stat.st_mode & S_IFDIR) {
+ fullpath = malloc(strlen(full_parent_name) + strlen(dirent->d_name)+2);
+ if (fullpath) {
+ sprintf(fullpath, "%s/%s", full_parent_name, dirent->d_name);
+ error = udfclient_rm_subtree(dir_node, entry_node, dirent->d_name, fullpath);
+ } else {
+ error = ENOMEM;
+ }
+ free(fullpath);
+ } else {
+ error = udf_remove_file(dir_node, entry_node, dirent->d_name);
+ if (!error)
+ printf("rm %s/%s\n", full_parent_name, dirent->d_name);
+ }
+ if (error)
+ break;
+ }
+ } while (!eof);
+
+ free(buffer);
+ free(fid);
+
+ /* leaving directory -> delete directory itself */
+ if (!error) {
+ error = udf_remove_directory(parent_node, dir_node, name);
+ if (!error) printf("rmdir %s/%s\n", full_parent_name, name);
+ }
+ return error;
+ }
+
+ return ENOTDIR;
+}
+#undef RM_SUBTREE_DIR_BUFFER_SIZE
+
+
+
+void udfclient_rm(int args, char *argv[]) {
+ struct udf_node *remove_node, *parent_node;
+ struct stat stat;
+ char *target_name, *leaf_name, *full_parent_name;
+ int error, len, arg;
+
+ if (args == 0) {
+ printf("Syntax: rm (file | dir)*\n");
+ return;
+ }
+
+ /* process all args; effectively multiplying an `rm' command */
+ arg = 0;
+ while (args) {
+ leaf_name = argv[arg];
+
+ /* lookup node; target_name gets substituted */
+ target_name = udfclient_realpath(curdir.name, leaf_name, &leaf_name);
+ error = udfclient_lookup_pathname(NULL, &remove_node, target_name);
+ if (error || !remove_node) {
+ printf("rm %s : %s\n", target_name, strerror(error));
+ free(target_name);
+ return; /* TODO continuation flag */
+ /* continue; */
+ }
+
+ full_parent_name = udfclient_realpath(target_name, "..", NULL);
+ error = udfclient_lookup_pathname(NULL, &parent_node, full_parent_name);
+ if (error || !parent_node) {
+ printf("rm %s : parent lookup failed : %s\n", target_name, strerror(error));
+ free(target_name);
+ free(full_parent_name);
+ return; /* TODO continuation flag */
+ /* continue; */
+ }
+
+ error = udfclient_getattr(remove_node, &stat);
+ if (!error) {
+ if (stat.st_mode & S_IFDIR) {
+ len = strlen(target_name);
+ if (target_name[len-1] == '/') target_name[len-1] = '\0';
+ error = udfclient_rm_subtree(parent_node, remove_node, leaf_name, target_name);
+ } else {
+ error = udf_remove_file(parent_node, remove_node, leaf_name);
+ if (!error) printf("rm %s/%s\n", full_parent_name, leaf_name);
+ }
+ }
+ if (error)
+ fprintf(stderr, "While removing file/dir : %s\n", strerror(error));
+
+ free(target_name);
+ free(full_parent_name);
+
+ if (error)
+ break; /* TODO continuation flag */
+
+ /* advance */
+ arg++;
+ args--;
+ }
+}
+
+
+void udfclient_mv(int args, char *from, char *to) {
+ struct udf_node *rename_me, *present, *old_parent, *new_parent;
+ char *rename_from_name, *rename_to_name, *old_parent_name, *new_parent_name;
+ int error;
+
+ if (args != 2) {
+ printf("Syntax: mv source destination\n");
+ return;
+ }
+
+ /* `from' gets substituted by its leaf name */
+ rename_from_name = udfclient_realpath(curdir.name, from, &from);
+ error = udfclient_lookup_pathname(NULL, &rename_me, rename_from_name);
+ if (error || !rename_me) {
+ printf("Can't find file/dir to be renamed\n");
+ free(rename_from_name);
+ return;
+ }
+
+ old_parent_name = udfclient_realpath(rename_from_name, "..", NULL);
+ error = udfclient_lookup_pathname(NULL, &old_parent, old_parent_name);
+ if (error || !old_parent) {
+ printf("Can't determine rootdir of renamed file?\n");
+ free(rename_from_name);
+ free(old_parent_name);
+ return;
+ }
+
+ /* `to' gets substituted by its leaf name */
+ rename_to_name = udfclient_realpath(curdir.name, to, &to);
+ udfclient_lookup_pathname(NULL, &present, rename_to_name);
+ new_parent_name = udfclient_realpath(rename_to_name, "..", NULL);
+ error = udfclient_lookup_pathname(NULL, &new_parent, new_parent_name);
+ if (error || !new_parent) {
+ printf("Can't determine rootdir of destination\n");
+ free(rename_from_name);
+ free(rename_to_name);
+ free(old_parent_name);
+ free(new_parent_name);
+ return;
+ }
+
+ error = udf_rename(old_parent, rename_me, from, new_parent, present, to);
+ if (error) {
+ printf("Can't move file or directory: %s\n", strerror(error));
+ return;
+ }
+
+ free(rename_from_name);
+ free(rename_to_name);
+ free(old_parent_name);
+ free(new_parent_name);
+}
+
+
+void udfclient_mkdir(int args, char *arg1) {
+ struct stat stat;
+ struct udf_node *udf_node, *parent_node;
+ char *full_create_name, *dirname, *parent_name;
+ int error;
+
+ if (args != 1) {
+ printf("Syntax: mkdir dir\n");
+ return;
+ }
+
+ /* get full name of dir to be created */
+ full_create_name = udfclient_realpath(curdir.name, arg1, &dirname);
+ parent_name = udfclient_realpath(full_create_name, "..", NULL);
+ error = udfclient_lookup_pathname(NULL, &parent_node, parent_name);
+ if (error || !parent_node) {
+ printf("Can't determine directory the new directory needs to be created in %d <%s> <%s> <%s>\n", error, parent_name, full_create_name, curdir.name);
+ free(full_create_name);
+ free(parent_name);
+ return;
+ }
+
+ bzero(&stat, sizeof(struct stat));
+ stat.st_uid = UINT_MAX;
+ stat.st_gid = UINT_MAX;
+ stat.st_mode = 0755 | S_IFDIR; /* don't forget this! */
+
+ error = udf_create_directory(parent_node, dirname, &stat, &udf_node);
+ if (error) {
+ printf("Can't create directory %s : %s\n", arg1, strerror(error));
+ }
+
+ free(full_create_name);
+ free(parent_name);
+}
+
+
+/* `line' gets more and more messed up in the proces */
+char *udfclient_get_one_arg(char *line, char **result) {
+ unsigned char chr, limiter;
+ char *end_arg;
+
+ *result = NULL;
+
+ /* get prepending whitespace */
+ while (*line && (*line <= ' ')) line++;
+
+ chr= '\0';
+ limiter = ' ';
+ if (*line == '"') {
+ line++;
+ limiter = '"';
+ }
+
+ *result = line;
+
+ while (*line) {
+ chr = *line;
+ if (chr && (chr < ' ')) chr = ' ';
+ if (chr == 0 || chr == limiter) {
+ break;
+ } else {
+ *line = chr;
+ }
+ line++;
+ }
+ end_arg = line;
+
+ if (chr == limiter) line++;
+
+ /* get appended whitespace */
+ while (*line && (*line <= ' ')) line++;
+
+ *end_arg = 0;
+
+ return line;
+}
+
+
+int udfclient_get_args(char *line, char *argv[]) {
+ int arg, args;
+
+ for (arg = 0; arg < MAX_ARGS+1; arg++) {
+ argv[arg] = "";
+ }
+
+ /* get all arguments */
+ args = 0;
+ while (args < MAX_ARGS+1) {
+ line = udfclient_get_one_arg(line, &argv[args]);
+ args++;
+ if (!*line) {
+ return args;
+ }
+ }
+
+ printf("UDFclient implementation limit: too many arguments\n");
+ return 0;
+}
+
+
+void udfclient_interact(void) {
+ int args, params;
+ char *cmd;
+ char *argv[MAX_ARGS+1];
+ char line[4096];
+
+ udfclient_pwd(0);
+ while (1) {
+ printf("UDF> ");
+ clearerr(stdin);
+
+ *line = 0;
+ fgets(line, 4096, stdin);
+
+ if ((*line == 0) && feof(stdin)) {
+ printf("quit\n");
+ return;
+ }
+
+ args = udfclient_get_args(line, argv);
+ cmd = argv[0];
+
+ params = args -1;
+ if (args) {
+ if (strcmp(cmd, "")==0) continue;
+
+ if (strcmp(cmd, "ls")==0) {
+ udfclient_ls(params, argv[1]);
+ continue;
+ }
+ if (strcmp(cmd, "cd")==0) {
+ udfclient_cd(params, argv[1]);
+ continue;
+ }
+ if (strcmp(cmd, "lcd")==0) {
+ udfclient_lcd(params, argv[1]);
+ continue;
+ }
+ if (strcmp(cmd, "lls")==0) {
+ udfclient_lls(params);
+ continue;
+ }
+ if (strcmp(cmd, "pwd")==0) {
+ udfclient_pwd(params);
+ continue;
+ }
+ if (strcmp(cmd, "free")==0) {
+ udfclient_free(params);
+ continue;
+ }
+ if (strcmp(cmd, "get")==0) {
+ udfclient_get(params, argv[1], argv[2]);
+ continue;
+ }
+ if (strcmp(cmd, "mget")==0) {
+ udfclient_mget(params, argv + 1);
+ continue;
+ }
+ if (strcmp(cmd, "put")==0) {
+ /* can get destination file/dir (one day) */
+ udfclient_put(params, argv[1], argv[2]);
+ continue;
+ }
+ if (strcmp(cmd, "mput")==0) {
+ udfclient_mput(params, argv + 1);
+ continue;
+ }
+ if (strcmp(cmd, "trunc")==0) {
+ udfclient_trunc(params, argv[1], argv[2]);
+ continue;
+ }
+ if (strcmp(cmd, "mkdir")==0) {
+ udfclient_mkdir(params, argv[1]);
+ continue;
+ }
+ if (strcmp(cmd, "rm")==0) {
+ udfclient_rm(params, argv + 1);
+ continue;
+ }
+ if (strcmp(cmd, "mv")==0) {
+ udfclient_mv(params, argv[1], argv[2]);
+ continue;
+ }
+ if (strcmp(cmd, "sync")==0) {
+ udfclient_sync();
+ continue;
+ }
+ if (strcmp(cmd, "help")==0) {
+ printf("Selected commands available (use \" pair for filenames with spaces) :\n"
+ "ls [file | dir]\tlists the UDF directory\n"
+ "cd [dir]\t\tchange current UDF directory\n"
+ "lcd [dir]\t\tchange current directory\n"
+ "lls\t\t\tlists current directory\n"
+ "pwd\t\t\tdisplay current directories\n"
+ "free\t\t\tdisplay free space on disc\n"
+ "get source [dest]\tretrieve a file / directory from disc\n"
+ "mget (file | dir)*\tretrieve set of files / directories\n"
+ "put source [dest]\twrite a file / directory to disc\n"
+ "mput (file | dir)*\twrite a set of files / directories\n"
+ "trunc file length\ttrunc file to length\n"
+ "mkdir dir\t\tcreate directory\n"
+ "rm (file | dir)*\tdelete set of files / directories\n"
+ "mv source dest\t\trename a file (limited)\n"
+ "sync\t\t\tsync filingsystem\n"
+ "quit\t\t\texits program\n"
+ "exit\t\t\talias for quit\n"
+ );
+ continue;
+ }
+ if (strcmp(cmd, "quit")==0 ||
+ strcmp(cmd, "exit")==0) {
+ return;
+ }
+ printf("\nUnknown command %s\n", cmd);
+ }
+ }
+}
+
+
+int usage(char *program) {
+ fprintf(stderr, "Usage: %s [options] devicename [devicename]*)\n", program);
+ fprintf(stderr, "-u level UDF system verbose level\n"
+ "-r range use only selected sessions like -3,5,7 or 6-\n"
+ "-W allow writing (temporary flag)\n"
+ "-F force mount writable when marked dirty (use with cause)\n"
+ "-b blocksize use alternative sectorsize; use only on files/discs\n"
+ "-D debug/verbose SCSI command errors\n"
+ "-s byteswap read sectors (for PVRs)\n"
+ );
+ return 1;
+}
+
+
+extern char *optarg;
+extern int optind;
+extern int optreset;
+
+
+int main(int argc, char *argv[]) {
+ struct udf_discinfo *disc, *next_disc;
+ uint32_t alt_sector_size;
+ char *progname, *range;
+ int flag, mnt_flags;
+ int error;
+
+ progname = argv[0];
+ if (argc == 1) {
+ return usage(progname);
+ }
+
+ /* be a bit more verbose */
+ udf_verbose = UDF_VERBLEV_ACTIONS;
+ uscsilib_verbose= 0;
+ mnt_flags = UDF_MNT_RDONLY;
+ range = NULL;
+ alt_sector_size = 0;
+ while ((flag = getopt(argc, argv, "u:Dr:WFb:s")) != -1) {
+ switch (flag) {
+ case 'u' :
+ udf_verbose = atoi(optarg);
+ break;
+ case 'D' :
+ uscsilib_verbose = 1;
+ break;
+ case 'r' :
+ range = strdup(optarg);
+ if (udf_check_session_range(range)) {
+ fprintf(stderr, "Invalid range %s\n", range);
+ return usage(progname);
+ }
+ break;
+ case 'W' :
+ mnt_flags &= ~UDF_MNT_RDONLY;
+ break;
+ case 'F' :
+ mnt_flags |= UDF_MNT_FORCE;
+ break;
+ case 'b' :
+ alt_sector_size = atoi(optarg);
+ break;
+ case 's' :
+ mnt_flags |= UDF_MNT_BSWAP;
+ break;
+ default :
+ return usage(progname);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0) return usage(progname);
+
+ if (!(mnt_flags & UDF_MNT_RDONLY)) {
+ printf("--------------------------------\n");
+ printf("WARNING: writing enabled, use on own risc\n");
+ printf("\t* DONT cancel program or data-loss might occure\n");
+ printf("\t* set dataspace userlimits very high when writing thousands of files\n");
+ printf("\nEnjoy your writing!\n");
+ printf("--------------------------------\n\n\n");
+ printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout); sleep(1); printf("%c", 7); fflush(stdout);
+ }
+
+ /* all other arguments are devices */
+ udf_init();
+ while (argc) {
+ printf("Opening device %s\n", *argv);
+ error = udf_mount_disc(*argv, range, alt_sector_size, mnt_flags, &disc);
+ if (error) {
+ fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error));
+ exit(1);
+ }
+ if (read_only) disc->recordable = 0;
+ if (read_only) disc->rewritable = 0;
+
+ argv++; argc--;
+ if (udf_verbose) printf("\n\n");
+ }
+
+ printf("\n");
+ printf("Resulting list of alive sets :\n\n");
+ udf_dump_alive_sets();
+
+ /* interactive part */
+ bzero(&curdir, sizeof(struct curdir));
+ curdir.mountpoint = NULL;
+ curdir.name = strdup("/");
+ udfclient_ls(0, "");
+
+ udfclient_interact();
+
+ /* close part */
+ printf("Closing discs\n");
+ disc = SLIST_FIRST(&udf_discs_list);
+ while (disc) {
+ next_disc = SLIST_NEXT(disc, next_disc);
+
+ udf_dismount_disc(disc);
+
+ disc = next_disc;
+ }
+
+ return 0;
+}
+
diff --git a/udfdump.c b/udfdump.c
new file mode 100644
index 0000000..1543c4f
--- /dev/null
+++ b/udfdump.c
@@ -0,0 +1,265 @@
+/* $NetBSD$ */
+
+/*
+ * File "udfdump.c" is part of the UDFclient toolkit.
+ * File $Id: udfdump.c,v 1.74 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include "udf.h"
+#include "udf_bswap.h"
+
+
+/* switches */
+
+/* #define DEBUG(a) (a) */
+#define DEBUG(a) if (0) { a; }
+int dump_sequential = 0;
+int dump_directory_trees = 0;
+
+#ifndef MAX
+# define MAX(a,b) ((a)>(b)?(a):(b))
+# define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+/* include the dump parts ... in order to get a more sane splitting */
+extern void udf_dump_descriptor(union dscrptr *dscrpt);
+extern void udf_dump_alive_sets(void);
+extern void udf_dump_root_dir(struct udf_mountpoint *mountpoint);
+
+
+/* globals */
+extern int udf_verbose;
+extern int uscsilib_verbose;
+
+
+/* main discdump routine */
+
+void udf_dump_sequential(int alt_sector_size, int flags, char *dev_name) {
+ struct udf_discinfo *disc;
+ union dscrptr *dscr;
+ uint64_t sec_num, rsec_num;
+ uint32_t sector_size, size, chunk, dscr_len;
+ uint8_t *new_sector, *sector, *pos;
+ int error;
+
+ udf_verbose = UDF_VERBLEV_ACTIONS;
+
+ printf("Opening disc:\n");
+ error = udf_open_disc(dev_name, flags, &disc);
+ if (error) return;
+
+ if (alt_sector_size)
+ disc->sector_size = alt_sector_size;
+
+ sector = calloc(1, 65*1024);
+ assert(sector);
+
+ printf("Sequential dump of device/file (sector size %d)\n", disc->sector_size);
+ sec_num = 0;
+ sector_size = disc->sector_size;
+ do {
+ size = 1;
+ error = udf_read_physical_sectors(disc, sec_num, size, "sector", sector);
+
+ if (!error) {
+ dscr = (union dscrptr *) sector;
+ if (!udf_check_tag(dscr)) {
+ if (udf_rw16(dscr->tag.id) <= TAGID_MAX) {
+ dscr_len = udf_calc_tag_malloc_size(dscr, sector_size);
+ dscr_len = MAX(sector_size, dscr_len);
+ if (dscr_len > sector_size) {
+ size = (dscr_len + sector_size -1) / sector_size;
+ new_sector = realloc(sector, size * sector_size);
+ if (!new_sector)
+ goto next_sector;
+ if (new_sector)
+ sector = new_sector;
+
+ dscr = (union dscrptr *) sector;
+ chunk = MIN(size, 0xffff);
+ pos = sector;
+ rsec_num = sec_num;
+ while (size) {
+ error = udf_read_physical_sectors(
+ disc, rsec_num, chunk, "sector", pos);
+ if (error)
+ break;
+ pos += chunk * sector_size;
+ rsec_num += chunk;
+ size -= chunk;
+ chunk = MIN(size, 0xffff);
+ }
+ if (error)
+ break;
+ }
+ size = (dscr_len + sector_size -1) / sector_size;
+
+ if (!udf_check_tag_payload(dscr)) {
+ printf("%8d ", (int) sec_num); udf_dump_descriptor(dscr);
+ }
+ }
+ }
+ } else {
+ if (error == ENOENT)
+ break;
+ printf("\nRead error : %s\n", strerror(error));
+ }
+next_sector:
+ sec_num++;
+ printf("\r%08d ", (int) sec_num);
+ fflush(stdout);
+ } while (1);
+ printf("\n");
+}
+
+
+void udf_dump_mountable_dirtrees(void) {
+ struct udf_mountpoint *mountpoint;
+
+ printf("Dumping directory tree for each non obsolete logical volume's fileset\n");
+ SLIST_FOREACH(mountpoint, &udf_mountables, all_next) {
+ printf("\n");
+ printf("Directory tree of mount point %s\n", mountpoint->mount_name);
+ udf_dump_root_dir(mountpoint);
+ }
+}
+
+
+int usage(char *program) {
+ fprintf(stderr, "Usage %s [options] devicename [devicename]*)\n", program);
+ fprintf(stderr, "-S dump device sequential\n"
+ "-s byteswap read sectors (for PVRs)\n"
+ "-t dump complete directory contents\n"
+ "-b blocksize use alternative sectorsize; use only on files/discs\n"
+ "-u level UDF system verbose level\n"
+ "-D verbose SCSI command errors\n"
+ "-r range only use UDF sessions range like -3,4-5 or 6-\n"
+ );
+ return 1;
+}
+
+
+int main(int argc, char *argv[]) {
+ struct udf_discinfo *disc;
+ char *progname, *range;
+ uint32_t alt_sector_size;
+ int min_session_number, max_session_number;
+ int flag, mntflags, bswap, error;
+
+ progname = argv[0];
+ if (argc == 1) return usage(progname);
+
+ dump_sequential = 0;
+ dump_directory_trees = 0;
+ range = NULL;
+ alt_sector_size = 0;
+ bswap = 0;
+ while ((flag = getopt(argc, argv, "u:r:b:DSst")) != -1) {
+ switch (flag) {
+ case 'u' :
+ udf_verbose = atoi(optarg);
+ break;
+ case 'r' :
+ range = strdup(optarg);
+ if (udf_check_session_range(range)) {
+ fprintf(stderr, "Invalid range %s\n", range);
+ return usage(progname);
+ }
+ break;
+ case 'b' :
+ alt_sector_size = atoi(optarg);
+ break;
+ case 'D' :
+ uscsilib_verbose = 1;
+ break;
+ case 'S' :
+ dump_sequential = 1;
+ break;
+ case 's' :
+ bswap = 1;
+ break;
+ case 't' :
+ dump_directory_trees = 1;
+ break;
+ default :
+ return usage(progname);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (dump_sequential) {
+ if (argc != 1) return usage(progname);
+ udf_dump_sequential(alt_sector_size, bswap, *argv);
+ return 0;
+ }
+
+ if (argc == 0) return usage(progname);
+
+ min_session_number = 0;
+ max_session_number = 999;
+
+ /* all other arguments are devices */
+ udf_init();
+ while (argc) {
+ printf("Opening device %s\n\n", *argv);
+ mntflags = UDF_MNT_RDONLY;
+ mntflags |= bswap ? UDF_MNT_BSWAP : 0;
+ error = udf_mount_disc(*argv, range, alt_sector_size, mntflags, &disc);
+ if (error) {
+ fprintf(stderr, "Can't open my device; bailing out : %s\n", strerror(error));
+ exit(1);
+ }
+
+ argv++; argc--;
+ if (udf_verbose) printf("\n\n");
+ }
+
+ printf("Resulting list of alive sets :\n\n");
+ udf_dump_alive_sets();
+
+ if (dump_directory_trees) {
+ printf("Dump tree of all mountables\n");
+ udf_dump_mountable_dirtrees();
+ printf("\n");
+ }
+
+ printf("Closing discs\n");
+ SLIST_FOREACH(disc, &udf_discs_list, next_disc) {
+ udf_close_disc(disc);
+ }
+
+ return 0;
+}
+
diff --git a/uio.c b/uio.c
new file mode 100644
index 0000000..0ade775
--- /dev/null
+++ b/uio.c
@@ -0,0 +1,87 @@
+/* $NetBSD$ */
+
+/*
+ * File "uio.c" is part of the UDFclient toolkit.
+ * File $Id: uio.c,v 1.7 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#include <assert.h>
+#include <string.h>
+#include "uio.h"
+
+
+/* modelled after NetBSD's uiomove */
+int uiomove(void *buf, size_t amount, struct uio *uio) {
+ struct iovec *iov;
+ char *cp = buf;
+ size_t cnt;
+
+ assert(buf);
+ assert(uio);
+ assert(uio->uio_iov);
+
+ while (amount > 0 && uio->uio_resid > 0) {
+ iov = uio->uio_iov;
+ cnt = iov->iov_len;
+ if (cnt == 0) {
+ assert(uio->uio_iovcnt > 0);
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ if (uio->uio_iovcnt == 0) return 0; /* end of buffers */
+ continue;
+ }
+
+ /* not more than requested please */
+ if (cnt > amount) cnt = amount;
+
+ /* move the information */
+ if (uio->uio_rw == UIO_READ) {
+ /* read into the uio */
+ memcpy(cp, iov->iov_base, cnt);
+ } else {
+ /* write from the uio */
+ memcpy(iov->iov_base, cp, cnt);
+ }
+
+ /* update the uio structure to reflect move */
+ iov->iov_base = (caddr_t) iov->iov_base + cnt;
+ iov->iov_len -= cnt;
+ uio->uio_resid -= cnt;
+ uio->uio_offset += cnt;
+ cp += cnt;
+
+ assert(cnt <= amount);
+
+ amount -= cnt;
+ }
+
+ return 0;
+}
+
+
+/* end of uio.c */
+
diff --git a/uio.h b/uio.h
new file mode 100644
index 0000000..4250050
--- /dev/null
+++ b/uio.h
@@ -0,0 +1,72 @@
+/* $NetBSD$ */
+
+/*
+ * File "uio.h" is part of the UDFclient toolkit.
+ * File $Id: uio.h,v 1.6 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYS_UIO_H_
+#define _SYS_UIO_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+
+/* define an uio structure for fragmenting reading in and writing data out */
+/* modelled after BSD's kernel structures */
+enum uio_rw {
+ UIO_READ,
+ UIO_WRITE
+};
+
+
+struct iovec {
+ void *iov_base; /* base if this chunk */
+ size_t iov_len; /* length */
+};
+
+
+/* this is consumed !! i.e. keep a copy */
+struct uio {
+ struct iovec *uio_iov; /* pointer to array of iovecs */
+ int uio_iovcnt; /* number of iovecs in array */
+ off_t uio_offset; /* current offset */
+ size_t uio_resid; /* residual i/o count */
+ enum uio_rw uio_rw; /* read/write direction */
+ /* ... rest obmitted ... */
+};
+
+
+#endif /* _SYS_UIO_H_ */
+
+/* allways declare the functions */
+
+
+/* move data from/to a uio structure to a blob */
+extern int uiomove(void *buf, size_t amount, struct uio *uio);
+
+
+/* end of uio.h */
diff --git a/uscsi_sense.c b/uscsi_sense.c
new file mode 100644
index 0000000..6e50373
--- /dev/null
+++ b/uscsi_sense.c
@@ -0,0 +1,835 @@
+/* $NetBSD: scsipi_verbose.c,v 1.27 2005/05/29 22:00:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Small changes made by Reinoud Zandijk <reinoud@netbsd.org>
+ */
+
+/*
+ * SCSI sense interpretation. Lifted from src/sys/dev/scsipi/scsipi_verbose.c,
+ * and modified for userland.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "uscsilib.h"
+
+
+static const char *sense_keys[16] = {
+ "No Additional Sense",
+ "Recovered Error",
+ "Not Ready",
+ "Media Error",
+ "Hardware Error",
+ "Illegal Request",
+ "Unit Attention",
+ "Write Protected",
+ "Blank Check",
+ "Vendor Unique",
+ "Copy Aborted",
+ "Aborted Command",
+ "Equal Error",
+ "Volume Overflow",
+ "Miscompare Error",
+ "Reserved"
+};
+
+/*
+ * The current version of this list can be obtained from
+ * <ftp://ftp.t10.org/t10/drafts/spc3/asc-num.txt>
+ */
+
+static const struct {
+ unsigned char asc;
+ unsigned char ascq;
+ const char *description;
+} adesc[] = {
+{ 0x00, 0x00, "No Additional Sense Information" },
+{ 0x00, 0x01, "Filemark Detected" },
+{ 0x00, 0x02, "End-Of-Partition/Medium Detected" },
+{ 0x00, 0x03, "Setmark Detected" },
+{ 0x00, 0x04, "Beginning-Of-Partition/Medium Detected" },
+{ 0x00, 0x05, "End-Of-Data Detected" },
+{ 0x00, 0x06, "I/O Process Terminated" },
+{ 0x00, 0x11, "Audio Play Operation In Progress" },
+{ 0x00, 0x12, "Audio Play Operation Paused" },
+{ 0x00, 0x13, "Audio Play Operation Successfully Completed" },
+{ 0x00, 0x14, "Audio Play Operation Stopped Due to Error" },
+{ 0x00, 0x15, "No Current Audio Status To Return" },
+{ 0x00, 0x16, "Operation In Progress" },
+{ 0x00, 0x17, "Cleaning Requested" },
+{ 0x00, 0x18, "Erase Operation In Progress" },
+{ 0x00, 0x19, "Locate Operation In Progress" },
+{ 0x00, 0x1A, "Rewind Operation In Progress" },
+{ 0x00, 0x1B, "Set Capacity Operation In Progess" },
+{ 0x00, 0x1C, "Verify Operation In Progress" },
+{ 0x01, 0x00, "No Index/Sector Signal" },
+{ 0x02, 0x00, "No Seek Complete" },
+{ 0x03, 0x00, "Peripheral Device Write Fault" },
+{ 0x03, 0x01, "No Write Current" },
+{ 0x03, 0x02, "Excessive Write Errors" },
+{ 0x04, 0x00, "Logical Unit Not Ready, Cause Not Reportable" },
+{ 0x04, 0x01, "Logical Unit Is in Process Of Becoming Ready" },
+{ 0x04, 0x02, "Logical Unit Not Ready, Initialization Command Required" },
+{ 0x04, 0x03, "Logical Unit Not Ready, Manual Intervention Required" },
+{ 0x04, 0x04, "Logical Unit Not Ready, Format In Progress" },
+{ 0x04, 0x05, "Logical Unit Not Ready, Rebuild In Progress" },
+{ 0x04, 0x06, "Logical Unit Not Ready, Recalculation In Progress" },
+{ 0x04, 0x07, "Logical Unit Not Ready, Operation In Progress" },
+{ 0x04, 0x08, "Logical Unit Not Ready, Long Write In Progress" },
+{ 0x04, 0x09, "Logical Unit Not Ready, Self-Test In Progress" },
+{ 0x04, 0x0A, "Logical Unit Not Accessible, Asymmetric Access State "
+ "Transition" },
+{ 0x04, 0x0B, "Logical Unit Not Accessible, Target Port In Standby State" },
+{ 0x04, 0x0C, "Logical Unit Not Accessible, Target Port In Unavailable State" },
+{ 0x04, 0x10, "Logical Unit Not Ready, Auxiliary Memory Not Accessible" },
+{ 0x04, 0x11, "Logical Unit Not Ready, Notify (Enable_Spinup) Required" },
+{ 0x05, 0x00, "Logical Unit Does Not Respond To Selection" },
+{ 0x06, 0x00, "No Reference Position Found" },
+{ 0x07, 0x00, "Multiple Peripheral Devices Selected" },
+{ 0x08, 0x00, "Logical Unit Communication Failure" },
+{ 0x08, 0x01, "Logical Unit Communication Timeout" },
+{ 0x08, 0x02, "Logical Unit Communication Parity Error" },
+{ 0x08, 0x03, "Logical Unit Communication CRC Error" },
+{ 0x08, 0x04, "Unreachable Copy Target" },
+{ 0x09, 0x00, "Track Following Error" },
+{ 0x09, 0x01, "Tracking Servo Failure" },
+{ 0x09, 0x02, "Focus Servo Failure" },
+{ 0x09, 0x03, "Spindle Servo Failure" },
+{ 0x09, 0x04, "Head Select Fault" },
+{ 0x0A, 0x00, "Error Log Overflow" },
+{ 0x0B, 0x00, "Warning" },
+{ 0x0B, 0x01, "Warning - Specified Temperature Exceeded" },
+{ 0x0B, 0x02, "Warning - Enclosure Degraded" },
+{ 0x0C, 0x00, "Write Error" },
+{ 0x0C, 0x01, "Write Error - Recovered with Auto Reallocation" },
+{ 0x0C, 0x02, "Write Error - Auto Reallocate Failed" },
+{ 0x0C, 0x03, "Write Error - Recommend Reassignment" },
+{ 0x0C, 0x04, "Compression Check Miscompare Error" },
+{ 0x0C, 0x05, "Data Expansion Occurred During Compression" },
+{ 0x0C, 0x06, "Block Not Compressible" },
+{ 0x0C, 0x07, "Write Error - Recovery Needed" },
+{ 0x0C, 0x08, "Write Error - Recovery Failed" },
+{ 0x0C, 0x09, "Write Error - Loss Of Streaming" },
+{ 0x0C, 0x0A, "Write Error - Padding Blocks Added" },
+{ 0x0C, 0x0B, "Auxiliary Memory Write Error" },
+{ 0x0C, 0x0C, "Write Error - Unexpected Unsolicited Data" },
+{ 0x0C, 0x0D, "Write Error - Not Enough Unsolicited Data" },
+{ 0x0D, 0x00, "Error Detected By Third Party Temporary Initiator" },
+{ 0x0D, 0x01, "Third Party Device Failure" },
+{ 0x0D, 0x02, "Copy Target Device Not Reachable" },
+{ 0x0D, 0x03, "Incorrect Copy Target Device Type" },
+{ 0x0D, 0x04, "Copy Target Device Data Underrun" },
+{ 0x0D, 0x05, "Copy Target Device Data Overrun" },
+{ 0x0E, 0x00, "Invalid Information Unit" },
+{ 0x0E, 0x01, "Information Unit Too Short" },
+{ 0x0E, 0x02, "Information Unit Too Long" },
+{ 0x10, 0x00, "ID CRC Or ECC Error" },
+{ 0x11, 0x00, "Unrecovered Read Error" },
+{ 0x11, 0x01, "Read Retries Exhausted" },
+{ 0x11, 0x02, "Error Too Long To Correct" },
+{ 0x11, 0x03, "Multiple Read Errors" },
+{ 0x11, 0x04, "Unrecovered Read Error - Auto Reallocate Failed" },
+{ 0x11, 0x05, "L-EC Uncorrectable Error" },
+{ 0x11, 0x06, "CIRC Unrecovered Error" },
+{ 0x11, 0x07, "Data Re-synchronization Error" },
+{ 0x11, 0x08, "Incomplete Block Read" },
+{ 0x11, 0x09, "No Gap Found" },
+{ 0x11, 0x0A, "Miscorrected Error" },
+{ 0x11, 0x0B, "Uncorrected Read Error - Recommend Reassignment" },
+{ 0x11, 0x0C, "Uncorrected Read Error - Recommend Rewrite the Data" },
+{ 0x11, 0x0D, "De-Compression CRC Error" },
+{ 0x11, 0x0E, "Cannot Decompress Using Declared Algorithm" },
+{ 0x11, 0x0F, "Error Reading UPC/EAN Number" },
+{ 0x11, 0x10, "Error Reading ISRC Number" },
+{ 0x11, 0x11, "Read Error - Loss Of Streaming" },
+{ 0x11, 0x12, "Auxiliary Memory Read Error" },
+{ 0x11, 0x13, "Read Error - Failed Retransmission Request" },
+{ 0x12, 0x00, "Address Mark Not Found for ID Field" },
+{ 0x13, 0x00, "Address Mark Not Found for Data Field" },
+{ 0x14, 0x00, "Recorded Entity Not Found" },
+{ 0x14, 0x01, "Record Not Found" },
+{ 0x14, 0x02, "Filemark or Setmark Not Found" },
+{ 0x14, 0x03, "End-Of-Data Not Found" },
+{ 0x14, 0x04, "Block Sequence Error" },
+{ 0x14, 0x05, "Record Not Found - Recommend Reassignment" },
+{ 0x14, 0x06, "Record Not Found - Data Auto-Reallocated" },
+{ 0x14, 0x07, "Locate Operation Failure" },
+{ 0x15, 0x00, "Random Positioning Error" },
+{ 0x15, 0x01, "Mechanical Positioning Error" },
+{ 0x15, 0x02, "Positioning Error Detected By Read of Medium" },
+{ 0x16, 0x00, "Data Synchronization Mark Error" },
+{ 0x16, 0x01, "Data Sync Error - Data Rewritten" },
+{ 0x16, 0x02, "Data Sync Error - Recommend Rewrite" },
+{ 0x16, 0x03, "Data Sync Error - Data Auto-Reallocated" },
+{ 0x16, 0x04, "Data Sync Error - Recommend Reassignment" },
+{ 0x17, 0x00, "Recovered Data With No Error Correction Applied" },
+{ 0x17, 0x01, "Recovered Data With Retries" },
+{ 0x17, 0x02, "Recovered Data With Positive Head Offset" },
+{ 0x17, 0x03, "Recovered Data With Negative Head Offset" },
+{ 0x17, 0x04, "Recovered Data With Retries and/or CIRC Applied" },
+{ 0x17, 0x05, "Recovered Data Using Previous Sector ID" },
+{ 0x17, 0x06, "Recovered Data Without ECC - Data Auto-Reallocated" },
+{ 0x17, 0x07, "Recovered Data Without ECC - Recommend Reassignment" },
+{ 0x17, 0x08, "Recovered Data Without ECC - Recommend Rewrite" },
+{ 0x17, 0x09, "Recovered Data Without ECC - Data Rewritten" },
+{ 0x18, 0x00, "Recovered Data With Error Correction Applied" },
+{ 0x18, 0x01, "Recovered Data With Error Correction & Retries Applied" },
+{ 0x18, 0x02, "Recovered Data - Data Auto-Reallocated" },
+{ 0x18, 0x03, "Recovered Data With CIRC" },
+{ 0x18, 0x04, "Recovered Data With LEC" },
+{ 0x18, 0x05, "Recovered Data - Recommend Reassignment" },
+{ 0x18, 0x06, "Recovered Data - Recommend Rewrite" },
+{ 0x18, 0x07, "Recovered Data With ECC - Data Rewritten" },
+{ 0x18, 0x08, "Recovered Data With Linking" },
+{ 0x19, 0x00, "Defect List Error" },
+{ 0x19, 0x01, "Defect List Not Available" },
+{ 0x19, 0x02, "Defect List Error in Primary List" },
+{ 0x19, 0x03, "Defect List Error in Grown List" },
+{ 0x1A, 0x00, "Parameter List Length Error" },
+{ 0x1B, 0x00, "Synchronous Data Transfer Error" },
+{ 0x1C, 0x00, "Defect List Not Found" },
+{ 0x1C, 0x01, "Primary Defect List Not Found" },
+{ 0x1C, 0x02, "Grown Defect List Not Found" },
+{ 0x1D, 0x00, "Miscompare During Verify Operation" },
+{ 0x1E, 0x00, "Recovered ID with ECC Correction" },
+{ 0x1F, 0x00, "Partial Defect List Transfer" },
+{ 0x20, 0x00, "Invalid Command Operation Code" },
+{ 0x20, 0x01, "Access Denied - Initiator Pending-Enrolled" },
+{ 0x20, 0x02, "Access Denied - No Access Rights" },
+{ 0x20, 0x03, "Access Denied - Invalid Mgmt ID Key" },
+{ 0x20, 0x04, "Illegal Command While In Write Capable State" },
+{ 0x20, 0x06, "Illegal Command While In Explicit Address Mode" },
+{ 0x20, 0x07, "Illegal Command While In Implicit Address Mode" },
+{ 0x20, 0x08, "Access Denied - Enrollment Conflict" },
+{ 0x20, 0x09, "Access Denied - Invalid LU Identifer" },
+{ 0x20, 0x0A, "Access Denied - Invalid Proxy Token" },
+{ 0x20, 0x0B, "Access Denied - ACL LUN Conflict" },
+{ 0x21, 0x00, "Logical Block Address Out of Range" },
+{ 0x21, 0x01, "Invalid Element Address" },
+{ 0x21, 0x02, "Invalid Address For Write" },
+{ 0x22, 0x00, "Illegal Function (Use 20 00, 24 00, or 26 00)" },
+{ 0x24, 0x00, "Illegal Field in CDB" },
+{ 0x24, 0x01, "CDB Decryption Error" },
+{ 0x24, 0x04, "Security Audit Value Frozen" },
+{ 0x24, 0x05, "Security Working Key Frozen" },
+{ 0x24, 0x06, "Nonce Not Unique" },
+{ 0x24, 0x07, "Nonce Timestamp Out Of Range" },
+{ 0x25, 0x00, "Logical Unit Not Supported" },
+{ 0x26, 0x00, "Invalid Field In Parameter List" },
+{ 0x26, 0x01, "Parameter Not Supported" },
+{ 0x26, 0x02, "Parameter Value Invalid" },
+{ 0x26, 0x03, "Threshold Parameters Not Supported" },
+{ 0x26, 0x04, "Invalid Release Of Persistent Reservation" },
+{ 0x26, 0x05, "Data Decryption Error" },
+{ 0x26, 0x06, "Too Many Target Descriptors" },
+{ 0x26, 0x07, "Unsupported Target Descriptor Type Code" },
+{ 0x26, 0x08, "Too Many Segment Descriptors" },
+{ 0x26, 0x09, "Unsupported Segment Descriptor Type Code" },
+{ 0x26, 0x0A, "Unexpected Inexact Segment" },
+{ 0x26, 0x0B, "Inline Data Length Exceeded" },
+{ 0x26, 0x0C, "Invalid Operation For Copy Source Or Destination" },
+{ 0x26, 0x0D, "Copy Segment Granularity Violation" },
+{ 0x26, 0x0E, "Invalid Parameter While Port Is Enabled" },
+{ 0x27, 0x00, "Write Protected" },
+{ 0x27, 0x01, "Hardware Write Protected" },
+{ 0x27, 0x02, "Logical Unit Software Write Protected" },
+{ 0x27, 0x03, "Associated Write Protect" },
+{ 0x27, 0x04, "Persistent Write Protect" },
+{ 0x27, 0x05, "Permanent Write Protect" },
+{ 0x27, 0x06, "Conditional Write Protect" },
+{ 0x28, 0x00, "Not Ready To Ready Transition (Medium May Have Changed)" },
+{ 0x28, 0x01, "Import Or Export Element Accessed" },
+{ 0x29, 0x00, "Power On, Reset, or Bus Device Reset Occurred" },
+{ 0x29, 0x01, "Power On Occurred" },
+{ 0x29, 0x02, "SCSI Bus Reset Occurred" },
+{ 0x29, 0x03, "Bus Device Reset Function Occurred" },
+{ 0x29, 0x04, "Device Internal Reset" },
+{ 0x29, 0x05, "Transceiver Mode Changed To Single-Ended" },
+{ 0x29, 0x06, "Transceiver Mode Changed To LVD" },
+{ 0x29, 0x07, "I_T Nexus Loss Occurred" },
+{ 0x2A, 0x00, "Parameters Changed" },
+{ 0x2A, 0x01, "Mode Parameters Changed" },
+{ 0x2A, 0x02, "Log Parameters Changed" },
+{ 0x2A, 0x03, "Reservations Preempted" },
+{ 0x2A, 0x04, "Reservations Released" },
+{ 0x2A, 0x05, "Registrations Preempted" },
+{ 0x2A, 0x06, "Asymmetric Access State Changed" },
+{ 0x2A, 0x07, "Implicit Asymmetric Access State Transition Failed" },
+{ 0x2B, 0x00, "Copy Cannot Execute Since Host Cannot Disconnect" },
+{ 0x2C, 0x00, "Command Sequence Error" },
+{ 0x2C, 0x01, "Too Many Windows Specified" },
+{ 0x2C, 0x02, "Invalid Combination of Windows Specified" },
+{ 0x2C, 0x03, "Current Program Area Is Not Empty" },
+{ 0x2C, 0x04, "Current Program Area Is Empty" },
+{ 0x2C, 0x05, "Illegal Power Condition Request" },
+{ 0x2C, 0x06, "Persistent Prevent Conflict" },
+{ 0x2C, 0x07, "Previous Busy Status" },
+{ 0x2C, 0x08, "Previous Task Set Full Status" },
+{ 0x2C, 0x09, "Previous Reservation Conflict Status" },
+{ 0x2C, 0x0A, "Partition or Collection Contains User Objects" },
+{ 0x2D, 0x00, "Overwrite Error On Update In Place" },
+{ 0x2E, 0x00, "Insufficient Time For Operation" },
+{ 0x2F, 0x00, "Commands Cleared By Another Initiator" },
+{ 0x30, 0x00, "Incompatible Medium Installed" },
+{ 0x30, 0x01, "Cannot Read Medium - Unknown Format" },
+{ 0x30, 0x02, "Cannot Read Medium - Incompatible Format" },
+{ 0x30, 0x03, "Cleaning Cartridge Installed" },
+{ 0x30, 0x04, "Cannot Write Medium - Unknown Format" },
+{ 0x30, 0x05, "Cannot Write Medium - Incompatible Format" },
+{ 0x30, 0x06, "Cannot Format Medium - Incompatible Medium" },
+{ 0x30, 0x07, "Cleaning Failure" },
+{ 0x30, 0x08, "Cannot Write - Application Code Mismatch" },
+{ 0x30, 0x09, "Current Session Not Fixated For Append" },
+{ 0x30, 0x0A, "Cleaning Request Rejected" },
+{ 0x30, 0x10, "Medium Not Formatted" },
+{ 0x31, 0x00, "Medium Format Corrupted" },
+{ 0x31, 0x01, "Format Command Failed" },
+{ 0x31, 0x02, "Zoned Formatting Failed Due To Spare Linking" },
+{ 0x32, 0x00, "No Defect Spare Location Available" },
+{ 0x32, 0x01, "Defect List Update Failure" },
+{ 0x33, 0x00, "Tape Length Error" },
+{ 0x34, 0x00, "Enclosure Failure" },
+{ 0x35, 0x00, "Enclosure Services Failure" },
+{ 0x35, 0x01, "Unsupported Enclosure Function" },
+{ 0x35, 0x02, "Enclosure Services Unavailable" },
+{ 0x35, 0x03, "Enclosure Services Transfer Failed" },
+{ 0x35, 0x04, "Enclosure Services Transfer Refused" },
+{ 0x36, 0x00, "Ribbon, Ink, or Toner Failure" },
+{ 0x37, 0x00, "Rounded Parameter" },
+{ 0x39, 0x00, "Saving Parameters Not Supported" },
+{ 0x3A, 0x00, "Medium Not Present" },
+{ 0x3A, 0x01, "Medium Not Present - Tray Closed" },
+{ 0x3A, 0x02, "Medium Not Present - Tray Open" },
+{ 0x3A, 0x03, "Medium Not Present - Loadable" },
+{ 0x3A, 0x04, "Medium Not Present - Medium Auxiliary Memory Accessible" },
+{ 0x3B, 0x00, "Sequential Positioning Error" },
+{ 0x3B, 0x01, "Tape Position Error At Beginning-of-Medium" },
+{ 0x3B, 0x02, "Tape Position Error At End-of-Medium" },
+{ 0x3B, 0x03, "Tape or Electronic Vertical Forms Unit Not Ready" },
+{ 0x3B, 0x04, "Slew Failure" },
+{ 0x3B, 0x05, "Paper Jam" },
+{ 0x3B, 0x06, "Failed To Sense Top-Of-Form" },
+{ 0x3B, 0x07, "Failed To Sense Bottom-Of-Form" },
+{ 0x3B, 0x08, "Reposition Error" },
+{ 0x3B, 0x09, "Read Past End Of Medium" },
+{ 0x3B, 0x0A, "Read Past Beginning Of Medium" },
+{ 0x3B, 0x0B, "Position Past End Of Medium" },
+{ 0x3B, 0x0C, "Position Past Beginning Of Medium" },
+{ 0x3B, 0x0D, "Medium Destination Element Full" },
+{ 0x3B, 0x0E, "Medium Source Element Empty" },
+{ 0x3B, 0x0F, "End Of Medium Reached" },
+{ 0x3B, 0x11, "Medium Magazine Not Accessible" },
+{ 0x3B, 0x12, "Medium Magazine Removed" },
+{ 0x3B, 0x13, "Medium Magazine Inserted" },
+{ 0x3B, 0x14, "Medium Magazine Locked" },
+{ 0x3B, 0x15, "Medium Magazine Unlocked" },
+{ 0x3B, 0x16, "Mechanical Positioning Or Changer Error" },
+{ 0x3D, 0x00, "Invalid Bits In IDENTIFY Message" },
+{ 0x3E, 0x00, "Logical Unit Has Not Self-Configured Yet" },
+{ 0x3E, 0x01, "Logical Unit Failure" },
+{ 0x3E, 0x02, "Timeout On Logical Unit" },
+{ 0x3E, 0x03, "Logical Unit Failed Self-Test" },
+{ 0x3E, 0x04, "Logical Unit Unable To Update Self-Test Log" },
+{ 0x3F, 0x00, "Target Operating Conditions Have Changed" },
+{ 0x3F, 0x01, "Microcode Has Been Changed" },
+{ 0x3F, 0x02, "Changed Operating Definition" },
+{ 0x3F, 0x03, "INQUIRY Data Has Changed" },
+{ 0x3F, 0x04, "Component Device Attached" },
+{ 0x3F, 0x05, "Device Identifier Changed" },
+{ 0x3F, 0x06, "Redundancy Group Created Or Modified" },
+{ 0x3F, 0x07, "Redundancy Group Deleted" },
+{ 0x3F, 0x08, "Spare Created Or Modified" },
+{ 0x3F, 0x09, "Spare Deleted" },
+{ 0x3F, 0x0A, "Volume Set Created Or Modified" },
+{ 0x3F, 0x0B, "Volume Set Deleted" },
+{ 0x3F, 0x0C, "Volume Set Deassigned" },
+{ 0x3F, 0x0D, "Volume Set Reassigned" },
+{ 0x3F, 0x0E, "Reported LUNs Data Has Changed" },
+{ 0x3F, 0x0F, "Echo Buffer Overwritten" },
+{ 0x3F, 0x10, "Medium Loadable" },
+{ 0x3F, 0x11, "Medium Auxiliary Memory Accessible" },
+{ 0x40, 0x00, "RAM FAILURE (Should Use 40 NN)" },
+{ 0x41, 0x00, "Data Path FAILURE (Should Use 40 NN)" },
+{ 0x42, 0x00, "Power-On or Self-Test FAILURE (Should Use 40 NN)" },
+{ 0x43, 0x00, "Message Error" },
+{ 0x44, 0x00, "Internal Target Failure" },
+{ 0x45, 0x00, "Select Or Reselect Failure" },
+{ 0x46, 0x00, "Unsuccessful Soft Reset" },
+{ 0x47, 0x00, "SCSI Parity Error" },
+{ 0x47, 0x01, "Data Phase CRC Error Detected" },
+{ 0x47, 0x02, "SCSI Parity Error Detected During ST Data Phase" },
+{ 0x47, 0x03, "Information Unit iuCRC Error Detected" },
+{ 0x47, 0x04, "Asynchronous Information Protection Error Detected" },
+{ 0x47, 0x05, "Protocol Service CRC Error" },
+{ 0x47, 0x7F, "Some Commands Cleared by iSCSI Protocol Event" },
+{ 0x48, 0x00, "INITIATOR DETECTED ERROR Message Received" },
+{ 0x49, 0x00, "Invalid Message Error" },
+{ 0x4A, 0x00, "Command Phase Error" },
+{ 0x4B, 0x00, "Data Phase Error" },
+{ 0x4B, 0x01, "Illegal Target Port Transfer Tag Received" },
+{ 0x4B, 0x02, "Too Much Write Data" },
+{ 0x4B, 0x03, "ACK/NAK Timeout" },
+{ 0x4B, 0x04, "NAK Reveived" },
+{ 0x4B, 0x05, "Data Offset Error" },
+{ 0x4B, 0x06, "Initiator Response Timeout" },
+{ 0x4C, 0x00, "Logical Unit Failed Self-Configuration" },
+{ 0x4D, 0x00, "Tagged Overlapped Commands (NN = Queue Tag)" },
+{ 0x4E, 0x00, "Overlapped Commands Attempted" },
+{ 0x50, 0x00, "Write Append Error" },
+{ 0x50, 0x01, "Write Append Position Error" },
+{ 0x50, 0x02, "Position Error Related To Timing" },
+{ 0x51, 0x00, "Erase Failure" },
+{ 0x51, 0x01, "Erase Failure - Incomplete Erase Operation Detected" },
+{ 0x52, 0x00, "Cartridge Fault" },
+{ 0x53, 0x00, "Media Load or Eject Failed" },
+{ 0x53, 0x01, "Unload Tape Failure" },
+{ 0x53, 0x02, "Medium Removal Prevented" },
+{ 0x54, 0x00, "SCSI To Host System Interface Failure" },
+{ 0x55, 0x00, "System Resource Failure" },
+{ 0x55, 0x01, "System Buffer Full" },
+{ 0x55, 0x02, "Insufficient Reservation Resources" },
+{ 0x55, 0x03, "Insufficient Resources" },
+{ 0x55, 0x04, "Insufficient Registration Resources" },
+{ 0x55, 0x05, "Insufficient Access Control Resources" },
+{ 0x55, 0x06, "Auxiliary Memory Out Of Space" },
+{ 0x57, 0x00, "Unable To Recover Table-Of-Contents" },
+{ 0x58, 0x00, "Generation Does Not Exist" },
+{ 0x59, 0x00, "Updated Block Read" },
+{ 0x5A, 0x00, "Operator Request or State Change Input (Unspecified)" },
+{ 0x5A, 0x01, "Operator Medium Removal Requested" },
+{ 0x5A, 0x02, "Operator Selected Write Protect" },
+{ 0x5A, 0x03, "Operator Selected Write Permit" },
+{ 0x5B, 0x00, "Log Exception" },
+{ 0x5B, 0x01, "Threshold Condition Met" },
+{ 0x5B, 0x02, "Log Counter At Maximum" },
+{ 0x5B, 0x03, "Log List Codes Exhausted" },
+{ 0x5C, 0x00, "RPL Status Change" },
+{ 0x5C, 0x01, "Spindles Synchronized" },
+{ 0x5C, 0x02, "Spindles Not Synchronized" },
+{ 0x5D, 0x00, "Failure Prediction Threshold Exceeded" },
+{ 0x5D, 0x01, "Media Failure Prediction Threshold Exceeded" },
+{ 0x5D, 0x02, "Logical Unit Failure Prediction Threshold Exceeded" },
+{ 0x5D, 0x03, "Spare Area Exhaustion Prediction Threshold Exceeded" },
+{ 0x5D, 0x10, "Hardware Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x11, "Hardware Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x12, "Hardware Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x13, "Hardware Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x14, "Hardware Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x15, "Hardware Impending Failure Access Times Too High" },
+{ 0x5D, 0x16, "Hardware Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x17, "Hardware Impending Failure Channel Parametrics" },
+{ 0x5D, 0x18, "Hardware Impending Failure Controller Detected" },
+{ 0x5D, 0x19, "Hardware Impending Failure Throughput Performance" },
+{ 0x5D, 0x1A, "Hardware Impending Failure Seek Time Performance" },
+{ 0x5D, 0x1B, "Hardware Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x1C, "Hardware Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0x20, "Controller Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x21, "Controller Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x22, "Controller Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x23, "Controller Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x24, "Controller Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x25, "Controller Impending Failure Access Times Too High" },
+{ 0x5D, 0x26, "Controller Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x27, "Controller Impending Failure Channel Parametrics" },
+{ 0x5D, 0x28, "Controller Impending Failure Controller Detected" },
+{ 0x5D, 0x29, "Controller Impending Failure Throughput Performance" },
+{ 0x5D, 0x2A, "Controller Impending Failure Seek Time Performance" },
+{ 0x5D, 0x2B, "Controller Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x2C, "Controller Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0x30, "Data Channel Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x31, "Data Channel Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x32, "Data Channel Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x33, "Data Channel Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x34, "Data Channel Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x35, "Data Channel Impending Failure Access Times Too High" },
+{ 0x5D, 0x36, "Data Channel Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x37, "Data Channel Impending Failure Channel Parametrics" },
+{ 0x5D, 0x38, "Data Channel Impending Failure Controller Detected" },
+{ 0x5D, 0x39, "Data Channel Impending Failure Throughput Performance" },
+{ 0x5D, 0x3A, "Data Channel Impending Failure Seek Time Performance" },
+{ 0x5D, 0x3B, "Data Channel Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x3C, "Data Channel Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0x40, "Servo Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x41, "Servo Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x42, "Servo Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x43, "Servo Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x44, "Servo Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x45, "Servo Impending Failure Access Times Too High" },
+{ 0x5D, 0x46, "Servo Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x47, "Servo Impending Failure Channel Parametrics" },
+{ 0x5D, 0x48, "Servo Impending Failure Controller Detected" },
+{ 0x5D, 0x49, "Servo Impending Failure Throughput Performance" },
+{ 0x5D, 0x4A, "Servo Impending Failure Seek Time Performance" },
+{ 0x5D, 0x4B, "Servo Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x4C, "Servo Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0x50, "Spindle Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x51, "Spindle Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x52, "Spindle Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x53, "Spindle Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x54, "Spindle Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x55, "Spindle Impending Failure Access Times Too High" },
+{ 0x5D, 0x56, "Spindle Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x57, "Spindle Impending Failure Channel Parametrics" },
+{ 0x5D, 0x58, "Spindle Impending Failure Controller Detected" },
+{ 0x5D, 0x59, "Spindle Impending Failure Throughput Performance" },
+{ 0x5D, 0x5A, "Spindle Impending Failure Seek Time Performance" },
+{ 0x5D, 0x5B, "Spindle Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x5C, "Spindle Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0x60, "Firmware Impending Failure General Hard Drive Failure" },
+{ 0x5D, 0x61, "Firmware Impending Failure Drive Error Rate Too High" },
+{ 0x5D, 0x62, "Firmware Impending Failure Data Error Rate Too High" },
+{ 0x5D, 0x63, "Firmware Impending Failure Seek Error Rate Too High" },
+{ 0x5D, 0x64, "Firmware Impending Failure Too Many Block Reassigns" },
+{ 0x5D, 0x65, "Firmware Impending Failure Access Times Too High" },
+{ 0x5D, 0x66, "Firmware Impending Failure Start Unit Times Too High" },
+{ 0x5D, 0x67, "Firmware Impending Failure Channel Parametrics" },
+{ 0x5D, 0x68, "Firmware Impending Failure Controller Detected" },
+{ 0x5D, 0x69, "Firmware Impending Failure Throughput Performance" },
+{ 0x5D, 0x6A, "Firmware Impending Failure Seek Time Performance" },
+{ 0x5D, 0x6B, "Firmware Impending Failure Spin-Up Retry Count" },
+{ 0x5D, 0x6C, "Firmware Impending Failure Drive Calibration Retry Count" },
+{ 0x5D, 0xFF, "Failure Prediction Threshold Exceeded (False)" },
+{ 0x5E, 0x00, "Low Power Condition On" },
+{ 0x5E, 0x01, "Idle Condition Activated By Timer" },
+{ 0x5E, 0x02, "Standby Condition Activated By Timer" },
+{ 0x5E, 0x03, "Idle Condition Activated By Command" },
+{ 0x5E, 0x04, "Standby Condition Activated By Command" },
+{ 0x5E, 0x41, "Power State Change To Active" },
+{ 0x5E, 0x42, "Power State Change To Idle" },
+{ 0x5E, 0x43, "Power State Change To Standby" },
+{ 0x5E, 0x45, "Power State Change To Sleep" },
+{ 0x5E, 0x47, "Power State Change To Device Control" },
+{ 0x60, 0x00, "Lamp Failure" },
+{ 0x61, 0x00, "Video Acquisition Error" },
+{ 0x61, 0x01, "Unable To Acquire Video" },
+{ 0x61, 0x02, "Out Of Focus" },
+{ 0x62, 0x00, "Scan Head Positioning Error" },
+{ 0x63, 0x00, "End Of User Area Encountered On This Track" },
+{ 0x63, 0x01, "Packet Does Not Fit In Available Space" },
+{ 0x64, 0x00, "Illegal Mode For This Track" },
+{ 0x64, 0x01, "Invalid Packet Size" },
+{ 0x65, 0x00, "Voltage Fault" },
+{ 0x66, 0x00, "Automatic Document Feeder Cover Up" },
+{ 0x66, 0x01, "Automatic Document Feeder Lift Up" },
+{ 0x66, 0x02, "Document Jam In Automatic Document Feeder" },
+{ 0x66, 0x03, "Document Misfeed In Automatic Document Feeder" },
+{ 0x67, 0x00, "Configuration Failure" },
+{ 0x67, 0x01, "Configuration Of Incapable Logical Units Failed" },
+{ 0x67, 0x02, "Add Logical Unit Failed" },
+{ 0x67, 0x03, "Modification Of Logical Unit Failed" },
+{ 0x67, 0x04, "Exchange Of Logical Unit Failed" },
+{ 0x67, 0x05, "Remove Of Logical Unit Failed" },
+{ 0x67, 0x06, "Attachment Of Logical Unit Failed" },
+{ 0x67, 0x07, "Creation of Logical Unit Failed" },
+{ 0x67, 0x08, "Assign Failure Occurred" },
+{ 0x67, 0x09, "Multiply Assigned Logical Unit" },
+{ 0x67, 0x0A, "Set Target Port Groups Command Failed" },
+{ 0x68, 0x00, "Logical Unit Not Configured" },
+{ 0x69, 0x00, "Data Loss On Logical Unit" },
+{ 0x69, 0x01, "Multiple Logical Unit Failures" },
+{ 0x69, 0x02, "Parity/Data Mismatch" },
+{ 0x6A, 0x00, "Informational, Refer To Log" },
+{ 0x6B, 0x00, "State Change Has Occurred" },
+{ 0x6B, 0x01, "Redundancy Level Got Better" },
+{ 0x6B, 0x02, "Redundancy Level Got Worse" },
+{ 0x6C, 0x00, "Rebuild Failure Occurred" },
+{ 0x6D, 0x00, "Recalculate Failure Occurred" },
+{ 0x6E, 0x00, "Command To Logical Unit Failed" },
+{ 0x6F, 0x00, "Copy Protection Key Exchange Failure - Authentication Failure" },
+{ 0x6F, 0x01, "Copy Protection Key Exchange Failure - Key Not Present" },
+{ 0x6F, 0x02, "Copy Protection Key Exchange Failure - Key Not Established" },
+{ 0x6F, 0x03, "Read Of Scrambled Sector Without Authentication" },
+{ 0x6F, 0x04, "Media Region Code Is Mismatched To Logical Unit Region" },
+{ 0x6F, 0x05, "Drive Region Must Be Permanent/Region Reset Count Error" },
+{ 0x70, 0x00, "Decompression Exception Short Algorithm ID Of NN" },
+{ 0x71, 0x00, "Decompression Exception Long Algorithm ID" },
+{ 0x72, 0x00, "Session Fixation Error" },
+{ 0x72, 0x01, "Session Fixation Error Writing Lead-In" },
+{ 0x72, 0x02, "Session Fixation Error Writing Lead-Out" },
+{ 0x72, 0x03, "Session Fixation Error - Incomplete Track In Session" },
+{ 0x72, 0x04, "Empty Or Partially Written Reserved Track" },
+{ 0x72, 0x05, "No More Track Reservations Allowed" },
+{ 0x73, 0x00, "CD Control Error" },
+{ 0x73, 0x01, "Power Calibration Area Almost Full" },
+{ 0x73, 0x02, "Power Calibration Area Is Full" },
+{ 0x73, 0x03, "Power Calibration Area Error" },
+{ 0x73, 0x04, "Program Memory Area Update Failure" },
+{ 0x73, 0x05, "Program Memory Area Is Full" },
+{ 0x73, 0x06, "RMA/PMA Is Almost Full" },
+{ 0x00, 0x00, NULL }
+};
+
+
+/* needs to move to a compat.c one day */
+#ifdef NO_STRLCPY
+
+size_t
+strlcpy(char *dst, const char *src, size_t size)
+{
+ strncpy(dst, src, size-1);
+ dst[size] = '\0';
+
+ return strlen(src);
+}
+
+#endif
+
+
+static void
+asc2ascii(u_char asc, u_char ascq, char *result, size_t l)
+{
+ int i = 0;
+
+ while (adesc[i].description != NULL) {
+ if (adesc[i].asc == asc && adesc[i].ascq == ascq)
+ break;
+ i++;
+ }
+ if (adesc[i].description == NULL) {
+ if (asc == 0x40 && ascq != 0)
+ (void) snprintf(result, l,
+ "Diagnostic Failure on Component 0x%02x",
+ ascq & 0xff);
+ else
+ (void) snprintf(result, l, "ASC 0x%02x ASCQ 0x%02x",
+ asc & 0xff, ascq & 0xff);
+ } else {
+ (void) strlcpy(result, adesc[i].description, l);
+ }
+}
+
+
+void
+uscsi_print_sense_data(uint8_t *s, int slen, int verbosity)
+{
+ int32_t info;
+ int i, j, k;
+ char *sbs;
+
+ /*
+ * Basics - print out SENSE KEY
+ */
+ printf(" SENSE KEY: %s", uscsi_decode_sense(s, 0));
+
+ /*
+ * Print out, unqualified but aligned, FMK, EOM and ILI status.
+ */
+ if (s[2] & 0xe0) {
+ char pad;
+ printf("\n ");
+ pad = ' ';
+ if (s[2] & SSD_FILEMARK) {
+ printf("%c Filemark Detected", pad);
+ pad = ',';
+ }
+ if (s[2] & SSD_EOM) {
+ printf("%c EOM Detected", pad);
+ pad = ',';
+ }
+ if (s[2] & SSD_ILI)
+ printf("%c Incorrect Length Indicator Set", pad);
+ }
+
+ /*
+ * Now we should figure out, based upon device type, how
+ * to format the information field. Unfortunately, that's
+ * not convenient here, so we'll print it as a signed
+ * 32 bit integer.
+ */
+ info = (s[3] << 24) | (s[4] << 16) | (s[5] << 8) | s[6];
+ if (info)
+ printf("\n INFO FIELD: %d", info);
+
+ /*
+ * Now we check additional length to see whether there is
+ * more information to extract.
+ */
+
+ /* enough for command specific information? */
+ if (((unsigned int) s[7]) < 4) {
+ printf("\n");
+ return;
+ }
+ info = (s[8] << 24) | (s[9] << 16) | (s[10] << 8) | s[11];
+ if (info)
+ printf("\n COMMAND INFO: %d (0x%x)", info, info);
+
+ /*
+ * Decode ASC && ASCQ info, plus FRU, plus the rest...
+ */
+
+ sbs = uscsi_decode_sense(s, 1);
+ if (sbs)
+ printf("\n ASC/ASCQ: %s", sbs);
+ if (s[14] != 0)
+ printf("\n FRU CODE: 0x%x", s[14] & 0xff);
+ sbs = uscsi_decode_sense(s, 3);
+ if (sbs)
+ printf("\n SKSV: %s", sbs);
+ printf("\n");
+ if (verbosity == 0) {
+ printf("\n");
+ return;
+ }
+
+ /*
+ * Now figure whether we should print any additional informtion.
+ *
+ * Where should we start from? If we had SKSV data,
+ * start from offset 18, else from offset 15.
+ *
+ * From that point until the end of the buffer, check for any
+ * nonzero data. If we have some, go back and print the lot,
+ * otherwise we're done.
+ */
+ if (sbs)
+ i = 18;
+ else
+ i = 15;
+ for (j = i; j < slen; j++)
+ if (s[j])
+ break;
+ if (j == slen)
+ return;
+
+ printf("\n Additional Sense Information (byte %d out...):\n", i);
+ if (i == 15) {
+ printf("\n\t%2d:", i);
+ k = 7;
+ } else {
+ printf("\n\t%2d:", i);
+ k = 2;
+ j -= 2;
+ }
+ while (j > 0) {
+ if (i >= slen)
+ break;
+ if (k == 8) {
+ k = 0;
+ printf("\n\t%2d:", i);
+ }
+ printf(" 0x%02x", s[i] & 0xff);
+ k++;
+ j--;
+ i++;
+ }
+ printf("\n\n");
+}
+
+
+char *
+uscsi_decode_sense(void *sinfo, int flag)
+{
+ unsigned char *snsbuf;
+ unsigned char skey;
+ static char rqsbuf[132];
+
+ skey = 0;
+
+ snsbuf = (unsigned char *) sinfo;
+ if (flag == 0 || flag == 2 || flag == 3)
+ skey = snsbuf[2] & 0xf;
+ if (flag == 0) { /* Sense Key Only */
+ (void) strlcpy(rqsbuf, sense_keys[skey], sizeof(rqsbuf));
+ return (rqsbuf);
+ } else if (flag == 1) { /* ASC/ASCQ Only */
+ asc2ascii(snsbuf[12], snsbuf[13], rqsbuf, sizeof(rqsbuf));
+ return (rqsbuf);
+ } else if (flag == 2) { /* Sense Key && ASC/ASCQ */
+ auto char localbuf[64];
+ asc2ascii(snsbuf[12], snsbuf[13], localbuf, sizeof(localbuf));
+ (void) snprintf(rqsbuf, sizeof(rqsbuf), "%s, %s",
+ sense_keys[skey], localbuf);
+ return (rqsbuf);
+ } else if (flag == 3 && snsbuf[7] >= 9 && (snsbuf[15] & 0x80)) {
+ /*
+ * SKSV Data
+ */
+ switch (skey) {
+ case SKEY_ILLEGAL_REQUEST:
+ if (snsbuf[15] & 0x8)
+ (void)snprintf(rqsbuf, sizeof(rqsbuf),
+ "Error in %s, Offset %d, bit %d",
+ (snsbuf[15] & 0x40)? "CDB" : "Parameters",
+ (snsbuf[16] & 0xff) << 8 |
+ (snsbuf[17] & 0xff), snsbuf[15] & 0x7);
+ else
+ (void)snprintf(rqsbuf, sizeof(rqsbuf),
+ "Error in %s, Offset %d",
+ (snsbuf[15] & 0x40)? "CDB" : "Parameters",
+ (snsbuf[16] & 0xff) << 8 |
+ (snsbuf[17] & 0xff));
+ return (rqsbuf);
+
+ case SKEY_RECOVERED_ERROR:
+ case SKEY_MEDIUM_ERROR:
+ case SKEY_HARDWARE_ERROR:
+ (void)snprintf(rqsbuf, sizeof(rqsbuf),
+ "Actual Retry Count: %d",
+ (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff));
+ return (rqsbuf);
+
+ case SKEY_NOT_READY:
+ (void)snprintf(rqsbuf, sizeof(rqsbuf),
+ "Progress Indicator: %d",
+ (snsbuf[16] & 0xff) << 8 | (snsbuf[17] & 0xff));
+ return (rqsbuf);
+
+ default:
+ break;
+ }
+ }
+ return (NULL);
+}
+
+
+void
+uscsi_print_sense(const char *name, u_char *req_cmd, int req_cmdlen,
+ u_char *req_sense, int req_senselen_used, int verbosity)
+{
+ int i;
+
+ printf("%s: Check Condition on CDB:", name);
+ for (i = 0; i < req_cmdlen; i++)
+ printf(" %02x", req_cmd[i]);
+ printf("\n");
+ uscsi_print_sense_data(req_sense, req_senselen_used, verbosity);
+}
+
diff --git a/uscsi_subr.c b/uscsi_subr.c
new file mode 100644
index 0000000..b96211a
--- /dev/null
+++ b/uscsi_subr.c
@@ -0,0 +1,594 @@
+/* $NetBSD: uscsi_subr.c,v 1.7 2002/10/08 20:17:06 soren Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace
+ * Simulation Facility, NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Small changes, generalisations and Linux support by Reinoud Zandijk
+ * <reinoud@netbsd.org>.
+ *
+ */
+
+
+/*
+ * SCSI support subroutines.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "uscsilib.h"
+
+
+int uscsilib_verbose = 0;
+
+
+#ifdef USCSI_SCSIPI
+ /*
+ * scsipi is a integrated SCSI and ATAPI layer under NetBSD and exists
+ * in a modified form under OpenBSD and possibly also under other
+ * operating systems.
+ */
+
+
+#include <sys/scsiio.h>
+#ifdef __OpenBSD__
+#include <scsi/scsi_all.h>
+#else
+#include <dev/scsipi/scsipi_all.h>
+#endif
+
+
+int
+uscsi_open(struct uscsi_dev *disc)
+{
+ struct stat stat;
+
+ disc->fhandle = open(disc->dev_name, O_RDWR, 0); /* no create */
+ if (disc->fhandle<0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+
+ if (fstat(disc->fhandle, &stat) < 0) {
+ perror("Can't stat device or file");
+ uscsi_close(disc);
+ return ENODEV;
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev * disc)
+{
+ close(disc->fhandle);
+ disc->fhandle = -1;
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen, void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ scsireq_t req;
+
+ memset(&req, 0, sizeof(req));
+ if (uscsi_sense)
+ bzero(uscsi_sense, sizeof(struct uscsi_sense));
+
+ memcpy(req.cmd, cmd, cmdlen);
+ req.cmdlen = cmdlen;
+ req.databuf = data;
+ req.datalen = datalen;
+ req.timeout = timeout;
+ req.flags = flags;
+ req.senselen = SENSEBUFLEN;
+
+ if (ioctl(disc->fhandle, SCIOCCOMMAND, &req) == -1)
+ return EIO;
+
+ if (req.retsts == SCCMD_OK)
+ return 0;
+
+ /* Some problem; report it and exit. */
+ if (req.retsts == SCCMD_TIMEOUT) {
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: SCSI command timed out\n",
+ disc->dev_name);
+ return EAGAIN;
+ } else if (req.retsts == SCCMD_BUSY) {
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: device is busy\n",
+ disc->dev_name);
+ return EBUSY;
+ } else if (req.retsts == SCCMD_SENSE) {
+ if (uscsi_sense) {
+ uscsi_sense->asc = req.sense[12];
+ uscsi_sense->ascq = req.sense[13];
+ uscsi_sense->skey_valid = req.sense[15] & 128;
+ uscsi_sense->sense_key = (req.sense[16] << 8) |
+ (req.sense[17]);
+ }
+ if (uscsilib_verbose)
+ uscsi_print_sense((char *) disc->dev_name,
+ req.cmd, req.cmdlen,
+ req.sense, req.senselen_used, 1);
+ return EIO;
+ } else
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: device had unknown status %x\n",
+ disc->dev_name,
+ req.retsts);
+
+ return EFAULT;
+}
+
+
+/*
+ * The reasoning behind this explicit copy is for compatibility with changes
+ * in our uscsi_addr structure.
+ */
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct scsi_addr raddr;
+ int error;
+
+ bzero(saddr, sizeof(struct scsi_addr));
+ error = ioctl(disc->fhandle, SCIOCIDENTIFY, &raddr);
+ if (error) {
+ saddr->type = USCSI_TYPE_UNKNOWN;
+ return error;
+ }
+
+#ifdef __NetBSD__
+ /* scsi and atapi are split up like in uscsi_addr */
+ if (raddr.type == 0) {
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.scbus = raddr.addr.scsi.scbus;
+ saddr->addr.scsi.target = raddr.addr.scsi.target;
+ saddr->addr.scsi.lun = raddr.addr.scsi.lun;
+ } else {
+ saddr->type = USCSI_TYPE_ATAPI;
+ saddr->addr.atapi.atbus = raddr.addr.atapi.atbus;
+ saddr->addr.atapi.drive = raddr.addr.atapi.drive;
+ }
+#endif
+#ifdef __OpenBSD__
+ /* atapi's are shown as SCSI devices */
+ if (raddr.type == 0) {
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.scbus = raddr.scbus;
+ saddr->addr.scsi.target = raddr.target;
+ saddr->addr.scsi.lun = raddr.lun;
+ } else {
+ saddr->type = USCSI_TYPE_ATAPI;
+ saddr->addr.atapi.atbus = raddr.scbus; /* overload */
+ saddr->addr.atapi.drive = raddr.target; /* overload */
+ }
+#endif
+
+ return 0;
+}
+
+#endif /* SCSILIB_SCSIPI */
+
+
+
+
+#ifdef USCSI_LINUX_SCSI
+ /*
+ * Support code for Linux SCSI code. It uses the ioctl() way of
+ * communicating since this is more close to the origional NetBSD
+ * scsipi implementation.
+ */
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+#define SENSEBUFLEN 48
+
+
+int
+uscsi_open(struct uscsi_dev * disc)
+{
+ int flags;
+ struct stat stat;
+
+ /* in Linux we are NOT allowed to open it blocking */
+ /* no create! */
+ disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (disc->fhandle < 0)
+ disc->fhandle = open(disc->dev_name, O_RDONLY | O_NONBLOCK, 0);
+ if (disc->fhandle < 0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+
+ /* explicitly mark it non blocking (again) (silly Linux) */
+ flags = fcntl(disc->fhandle, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ fcntl(disc->fhandle, F_SETFL, flags);
+
+ if (fstat(disc->fhandle, &stat) < 0) {
+ perror("Can't stat device or file");
+ uscsi_close(disc);
+ return ENODEV;
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev * disc)
+{
+ close(disc->fhandle);
+ disc->fhandle = -1;
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen,
+ void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ struct sg_io_hdr req;
+ uint8_t sense_buffer[SENSEBUFLEN];
+ int error;
+
+ bzero(&req, sizeof(req));
+ if (flags == SG_DXFER_FROM_DEV) bzero(data, datalen);
+
+ req.interface_id = 'S';
+ req.dxfer_direction = flags;
+ req.cmd_len = cmdlen;
+ req.mx_sb_len = SENSEBUFLEN;
+ req.iovec_count = 0;
+ req.dxfer_len = datalen;
+ req.dxferp = data;
+ req.cmdp = cmd;
+ req.sbp = sense_buffer;
+ req.flags = 0;
+ req.timeout = timeout;
+
+ error = ioctl(disc->fhandle, SG_IO, &req);
+
+ if (req.status) {
+ /* Is this OK? */
+ if (uscsi_sense) {
+ uscsi_sense->asc = sense_buffer[12];
+ uscsi_sense->ascq = sense_buffer[13];
+ uscsi_sense->skey_valid = sense_buffer[15] & 128;
+ uscsi_sense->sense_key = (sense_buffer[16] << 8) |
+ (sense_buffer[17]);
+ }
+ if (uscsilib_verbose) {
+ uscsi_print_sense((char *) disc->dev_name,
+ cmd, cmdlen, sense_buffer, req.sb_len_wr, 1);
+ }
+ }
+
+ return error;
+}
+
+
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct sg_scsi_id sg_scsi_id;
+ struct sg_id {
+ /* target | lun << 8 | channel << 16 | low_ino << 24 */
+ uint32_t tlci;
+ uint32_t uniq_id;
+ } sg_id;
+ int emulated;
+ int error;
+
+ /* clean result */
+ bzero(saddr, sizeof(struct uscsi_addr));
+
+ /* check if its really SCSI or emulated SCSI (ATAPI f.e.) */
+ saddr->type = USCSI_TYPE_SCSI;
+ ioctl(disc->fhandle, SG_EMULATED_HOST, &emulated);
+ if (emulated) saddr->type = USCSI_TYPE_ATAPI;
+
+ /* try 2.4 kernel or older */
+ error = ioctl(disc->fhandle, SG_GET_SCSI_ID, &sg_scsi_id);
+ if (!error) {
+ saddr->addr.scsi.target = sg_scsi_id.scsi_id;
+ saddr->addr.scsi.lun = sg_scsi_id.lun;
+ saddr->addr.scsi.scbus = sg_scsi_id.channel;
+
+ return 0;
+ }
+
+ /* 2.6 kernel or newer */
+ error = ioctl(disc->fhandle, SCSI_IOCTL_GET_IDLUN, &sg_id);
+ if (error) return error;
+
+ saddr->addr.scsi.target = (sg_id.tlci ) & 0xff;
+ saddr->addr.scsi.lun = (sg_id.tlci >> 8) & 0xff;
+ saddr->addr.scsi.scbus = (sg_id.tlci >> 16) & 0xff;
+
+ return 0;
+}
+
+#endif /* USCSI_LINUX_SCSI */
+
+
+
+
+#ifdef USCSI_FREEBSD_CAM
+
+int
+uscsi_open(struct uscsi_dev *disc)
+{
+ disc->devhandle = cam_open_device(disc->dev_name, O_RDWR);
+
+ if (disc->devhandle == NULL) {
+ disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (disc->fhandle < 0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev *disc)
+{
+ if (disc->devhandle != NULL) {
+ cam_close_device(disc->devhandle);
+ disc->devhandle = NULL;
+ } else {
+ close(disc->fhandle);
+ disc->fhandle = -1;
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen,
+ void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ struct cam_device *cam_dev;
+ struct scsi_sense_data *cam_sense_data;
+ union ccb ccb;
+ uint32_t cam_sense;
+ uint8_t *keypos;
+ int camflags;
+
+ memset(&ccb, 0, sizeof(ccb));
+ cam_dev = (struct cam_device *) disc->devhandle;
+ if (cam_dev == 0)
+ return EIO;
+
+ if (datalen == 0) flags = SCSI_NODATACMD;
+ /* optional : */
+ /* if (data) assert(flags == SCSI_NODATACMD); */
+
+ camflags = CAM_DIR_NONE;
+ if (flags & SCSI_READCMD)
+ camflags = CAM_DIR_IN;
+ if (flags & SCSI_WRITECMD)
+ camflags = CAM_DIR_OUT;
+
+ cam_fill_csio(
+ &ccb.csio,
+ 0, /* retries */
+ NULL, /* cbfcnp */
+ camflags, /* flags */
+ MSG_SIMPLE_Q_TAG, /* tag_action */
+ (u_int8_t *) data, /* data_ptr */
+ datalen, /* dxfer_len */
+ SSD_FULL_SIZE, /* sense_len */
+ cmdlen, /* cdb_len */
+ timeout /* timeout */
+ );
+
+ /* Disable freezing the device queue */
+ ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ memcpy(ccb.csio.cdb_io.cdb_bytes, cmd, cmdlen);
+
+ /* Send the command down via the CAM interface */
+ if (cam_send_ccb(cam_dev, &ccb) < 0) {
+ err(1, "cam_send_ccb");
+ }
+
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ return 0;
+
+ /* print error using the uscsi_sense routines? */
+
+ cam_sense = (ccb.ccb_h.status & (CAM_STATUS_MASK | CAM_AUTOSNS_VALID));
+ if (cam_sense != (CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID))
+ return EFAULT;
+
+ /* drive responds with sense information */
+ if (!uscsilib_verbose)
+ return EFAULT;
+
+ /* print sense info */
+ cam_sense_data = &ccb.csio.sense_data;
+ if (uscsi_sense) {
+ uscsi_sense->asc = cam_sense_data->add_sense_code;
+ uscsi_sense->ascq = cam_sense_data->add_sense_code_qual;
+ keypos = cam_sense_data->sense_key_spec;
+ uscsi_sense->skey_valid = keypos[0] & 128;
+ uscsi_sense->sense_key = (keypos[1] << 8) | (keypos[2]);
+ }
+
+ uscsi_print_sense((char *) disc->dev_name,
+ cmd, cmdlen,
+ (uint8_t *) cam_sense_data, 8 + cam_sense_data->extra_len, 1);
+
+ return EFAULT;
+}
+
+
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct cam_device *cam_dev;
+
+ /* clean result */
+ bzero(saddr, sizeof(struct uscsi_addr));
+
+ cam_dev = (struct cam_device *) disc->devhandle;
+ if (!cam_dev) return ENODEV;
+
+ /* check if its really SCSI or emulated SCSI (ATAPI f.e.) ? */
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.target = cam_dev->target_id;
+ saddr->addr.scsi.lun = cam_dev->target_lun;
+ saddr->addr.scsi.scbus = cam_dev->bus_id;
+
+ return 0;
+}
+
+
+#endif /* USCSI_FREEBSD_CAM */
+
+
+/*
+ * Checks if SCSI is available by issueing the obligatory INQUIRY.
+ */
+
+int
+uscsi_check_for_scsi(struct uscsi_dev *disc)
+{
+ scsicmd cmd;
+ uint8_t buf[256];
+ int error;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x12; /* INQUIRY */
+ cmd[1] = 0; /* basic inquiry */
+ cmd[2] = 0; /* no page or operation code */
+ cmd[3] = 0; /* reserved/MSB result */
+ cmd[4] = 96; /* all but vendor specific */
+ cmd[5] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc, cmd, 6, buf, 96, 30000, NULL);
+
+ return error;
+}
+
+
+/*
+ * Generic SCSI funtions also used by the sense printing functionality.
+ * FreeBSD support has it allready asked for by the CAM.
+ */
+int
+uscsi_mode_sense(struct uscsi_dev *dev,
+ uint8_t pgcode, uint8_t pctl, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(buf, len); /* initialise recieving buffer */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x1a; /* MODE SENSE */
+ cmd[ 1] = 0; /* - */
+ cmd[ 2] = pgcode | pctl; /* page code and control flags */
+ cmd[ 3] = 0; /* - */
+ cmd[ 4] = len; /* length of recieve buffer */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_READCMD, dev, &cmd, 6, buf, len, 10000, NULL);
+}
+
+
+int
+uscsi_mode_select(struct uscsi_dev *dev,
+ uint8_t byte2, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x15; /* MODE SELECT */
+ cmd[ 1] = 0x10 | byte2; /* SCSI-2 page format select */
+ cmd[ 4] = len; /* length of page settings */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
+ 10000, NULL);
+}
+
+
+int
+uscsi_request_sense(struct uscsi_dev *dev, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(buf, len); /* initialise recieving buffer */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x03; /* REQUEST SENSE */
+ cmd[ 4] = len; /* length of data to be read */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
+ 10000, NULL);
+}
+
+
+/* end of uscsi_subr.c */
+
diff --git a/uscsi_subr.c.async b/uscsi_subr.c.async
new file mode 100644
index 0000000..a7f0986
--- /dev/null
+++ b/uscsi_subr.c.async
@@ -0,0 +1,608 @@
+/* $NetBSD: uscsi_subr.c,v 1.7 2002/10/08 20:17:06 soren Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace
+ * Simulation Facility, NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Small changes, generalisations and Linux support by Reinoud Zandijk
+ * <reinoud@netbsd.org>.
+ *
+ */
+
+
+/*
+ * SCSI support subroutines.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "uscsilib.h"
+
+
+int uscsilib_verbose = 1;
+
+
+#ifdef USCSI_SCSIPI
+ /*
+ * scsipi is a integrated SCSI and ATAPI layer under NetBSD and exists
+ * in a modified form under OpenBSD and possibly also under other
+ * operating systems.
+ */
+
+
+#include <sys/scsiio.h>
+#ifdef __OpenBSD__
+#include <scsi/scsi_all.h>
+#else
+#include <dev/scsipi/scsipi_all.h>
+#endif
+
+
+int
+uscsi_open(struct uscsi_dev *disc)
+{
+ struct stat stat;
+ int flags, var;
+
+ flags = O_RDWR | O_NONBLOCK | O_ASYNC;
+ disc->fhandle = open(disc->dev_name, flags, 0); /* no create */
+ if (disc->fhandle<0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+
+ if (fstat(disc->fhandle, &stat) < 0) {
+ perror("Can't stat device or file");
+ uscsi_close(disc);
+ return ENODEV;
+ }
+ flags = fcntl(disc->fhandle, F_GETFL, &var);
+ if (flags & O_NONBLOCK)
+ fprintf(stderr, "Device did open with O_NONBLOCK\n");
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev * disc)
+{
+ close(disc->fhandle);
+ disc->fhandle = -1;
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen, void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ scsireq_t req;
+ int i;
+
+ memset(&req, 0, sizeof(req));
+ if (uscsi_sense)
+ bzero(uscsi_sense, sizeof(struct uscsi_sense));
+
+ memcpy(req.cmd, cmd, cmdlen);
+ req.cmdlen = cmdlen;
+ req.databuf = data;
+ req.datalen = datalen;
+ req.timeout = 1; //timeout;
+ req.flags = flags;
+ req.senselen = SENSEBUFLEN;
+
+ /* test if unit is ready */
+ for (i = 0; i < timeout/1; i++) {
+ if (ioctl(disc->fhandle, SCIOCCOMMAND, &req) == -1)
+ return EIO;
+
+ if (req.retsts != SCCMD_TIMEOUT)
+ break;
+ }
+ if (i > 0)
+ printf("took %d tries\n", i+1);
+
+ if (req.retsts == SCCMD_OK)
+ return 0;
+
+fprintf(stderr, "%s : req.retsts %d\n", __func__, req.retsts);
+ /* Some problem; report it and exit. */
+ if (req.retsts == SCCMD_TIMEOUT) {
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: SCSI command timed out\n",
+ disc->dev_name);
+ return EAGAIN;
+ } else if (req.retsts == SCCMD_BUSY) {
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: device is busy\n",
+ disc->dev_name);
+ return EBUSY;
+ } else if (req.retsts == SCCMD_SENSE) {
+ if (uscsi_sense) {
+ uscsi_sense->asc = req.sense[12];
+ uscsi_sense->ascq = req.sense[13];
+ uscsi_sense->skey_valid = req.sense[15] & 128;
+ uscsi_sense->sense_key = (req.sense[16] << 8) |
+ (req.sense[17]);
+ }
+ if (uscsilib_verbose)
+ uscsi_print_sense((char *) disc->dev_name,
+ req.cmd, req.cmdlen,
+ req.sense, req.senselen_used, 1);
+ return EIO;
+ } else
+ if (uscsilib_verbose)
+ fprintf(stderr, "%s: device had unknown status %x\n",
+ disc->dev_name,
+ req.retsts);
+
+ return EFAULT;
+}
+
+
+/*
+ * The reasoning behind this explicit copy is for compatibility with changes
+ * in our uscsi_addr structure.
+ */
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct scsi_addr raddr;
+ int error;
+
+ bzero(saddr, sizeof(struct scsi_addr));
+ error = ioctl(disc->fhandle, SCIOCIDENTIFY, &raddr);
+ if (error) {
+ saddr->type = USCSI_TYPE_UNKNOWN;
+ return error;
+ }
+
+#ifdef __NetBSD__
+ /* scsi and atapi are split up like in uscsi_addr */
+ if (raddr.type == 0) {
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.scbus = raddr.addr.scsi.scbus;
+ saddr->addr.scsi.target = raddr.addr.scsi.target;
+ saddr->addr.scsi.lun = raddr.addr.scsi.lun;
+ } else {
+ saddr->type = USCSI_TYPE_ATAPI;
+ saddr->addr.atapi.atbus = raddr.addr.atapi.atbus;
+ saddr->addr.atapi.drive = raddr.addr.atapi.drive;
+ }
+#endif
+#ifdef __OpenBSD__
+ /* atapi's are shown as SCSI devices */
+ if (raddr.type == 0) {
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.scbus = raddr.scbus;
+ saddr->addr.scsi.target = raddr.target;
+ saddr->addr.scsi.lun = raddr.lun;
+ } else {
+ saddr->type = USCSI_TYPE_ATAPI;
+ saddr->addr.atapi.atbus = raddr.scbus; /* overload */
+ saddr->addr.atapi.drive = raddr.target; /* overload */
+ }
+#endif
+
+ return 0;
+}
+
+#endif /* SCSILIB_SCSIPI */
+
+
+
+
+#ifdef USCSI_LINUX_SCSI
+ /*
+ * Support code for Linux SCSI code. It uses the ioctl() way of
+ * communicating since this is more close to the origional NetBSD
+ * scsipi implementation.
+ */
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+#define SENSEBUFLEN 48
+
+
+int
+uscsi_open(struct uscsi_dev * disc)
+{
+ int flags;
+ struct stat stat;
+
+ /* in Linux we are NOT allowed to open it blocking */
+ /* no create! */
+ disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (disc->fhandle < 0)
+ disc->fhandle = open(disc->dev_name, O_RDONLY | O_NONBLOCK, 0);
+ if (disc->fhandle < 0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+
+ /* explicitly mark it non blocking (again) (silly Linux) */
+ flags = fcntl(disc->fhandle, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ fcntl(disc->fhandle, F_SETFL, flags);
+
+ if (fstat(disc->fhandle, &stat) < 0) {
+ perror("Can't stat device or file");
+ uscsi_close(disc);
+ return ENODEV;
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev * disc)
+{
+ close(disc->fhandle);
+ disc->fhandle = -1;
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen,
+ void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ struct sg_io_hdr req;
+ uint8_t sense_buffer[SENSEBUFLEN];
+ int error;
+
+ bzero(&req, sizeof(req));
+ if (flags == SG_DXFER_FROM_DEV) bzero(data, datalen);
+
+ req.interface_id = 'S';
+ req.dxfer_direction = flags;
+ req.cmd_len = cmdlen;
+ req.mx_sb_len = SENSEBUFLEN;
+ req.iovec_count = 0;
+ req.dxfer_len = datalen;
+ req.dxferp = data;
+ req.cmdp = cmd;
+ req.sbp = sense_buffer;
+ req.flags = 0;
+ req.timeout = timeout;
+
+ error = ioctl(disc->fhandle, SG_IO, &req);
+
+ if (req.status) {
+ /* Is this OK? */
+ if (uscsi_sense) {
+ uscsi_sense->asc = sense_buffer[12];
+ uscsi_sense->ascq = sense_buffer[13];
+ uscsi_sense->skey_valid = sense_buffer[15] & 128;
+ uscsi_sense->sense_key = (sense_buffer[16] << 8) |
+ (sense_buffer[17]);
+ }
+ if (uscsilib_verbose) {
+ uscsi_print_sense((char *) disc->dev_name,
+ cmd, cmdlen, sense_buffer, req.sb_len_wr, 1);
+ }
+ }
+
+ return error;
+}
+
+
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct sg_scsi_id sg_scsi_id;
+ struct sg_id {
+ /* target | lun << 8 | channel << 16 | low_ino << 24 */
+ uint32_t tlci;
+ uint32_t uniq_id;
+ } sg_id;
+ int emulated;
+ int error;
+
+ /* clean result */
+ bzero(saddr, sizeof(struct uscsi_addr));
+
+ /* check if its really SCSI or emulated SCSI (ATAPI f.e.) */
+ saddr->type = USCSI_TYPE_SCSI;
+ ioctl(disc->fhandle, SG_EMULATED_HOST, &emulated);
+ if (emulated) saddr->type = USCSI_TYPE_ATAPI;
+
+ /* try 2.4 kernel or older */
+ error = ioctl(disc->fhandle, SG_GET_SCSI_ID, &sg_scsi_id);
+ if (!error) {
+ saddr->addr.scsi.target = sg_scsi_id.scsi_id;
+ saddr->addr.scsi.lun = sg_scsi_id.lun;
+ saddr->addr.scsi.scbus = sg_scsi_id.channel;
+
+ return 0;
+ }
+
+ /* 2.6 kernel or newer */
+ error = ioctl(disc->fhandle, SCSI_IOCTL_GET_IDLUN, &sg_id);
+ if (error) return error;
+
+ saddr->addr.scsi.target = (sg_id.tlci ) & 0xff;
+ saddr->addr.scsi.lun = (sg_id.tlci >> 8) & 0xff;
+ saddr->addr.scsi.scbus = (sg_id.tlci >> 16) & 0xff;
+
+ return 0;
+}
+
+#endif /* USCSI_LINUX_SCSI */
+
+
+
+
+#ifdef USCSI_FREEBSD_CAM
+
+int
+uscsi_open(struct uscsi_dev *disc)
+{
+ disc->devhandle = cam_open_device(disc->dev_name, O_RDWR);
+
+ if (disc->devhandle == NULL) {
+ disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0);
+ if (disc->fhandle < 0) {
+ perror("Failure to open device or file");
+ return ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_close(struct uscsi_dev *disc)
+{
+ if (disc->devhandle != NULL) {
+ cam_close_device(disc->devhandle);
+ disc->devhandle = NULL;
+ } else {
+ close(disc->fhandle);
+ disc->fhandle = -1;
+ }
+
+ return 0;
+}
+
+
+int
+uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen,
+ void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense)
+{
+ struct cam_device *cam_dev;
+ struct scsi_sense_data *cam_sense_data;
+ union ccb ccb;
+ uint32_t cam_sense;
+ uint8_t *keypos;
+ int camflags;
+
+ memset(&ccb, 0, sizeof(ccb));
+ cam_dev = (struct cam_device *) disc->devhandle;
+ if (cam_dev == 0)
+ return EIO;
+
+ if (datalen == 0) flags = SCSI_NODATACMD;
+ /* optional : */
+ /* if (data) assert(flags == SCSI_NODATACMD); */
+
+ camflags = CAM_DIR_NONE;
+ if (flags & SCSI_READCMD)
+ camflags = CAM_DIR_IN;
+ if (flags & SCSI_WRITECMD)
+ camflags = CAM_DIR_OUT;
+
+ cam_fill_csio(
+ &ccb.csio,
+ 0, /* retries */
+ NULL, /* cbfcnp */
+ camflags, /* flags */
+ MSG_SIMPLE_Q_TAG, /* tag_action */
+ (u_int8_t *) data, /* data_ptr */
+ datalen, /* dxfer_len */
+ SSD_FULL_SIZE, /* sense_len */
+ cmdlen, /* cdb_len */
+ timeout /* timeout */
+ );
+
+ /* Disable freezing the device queue */
+ ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ memcpy(ccb.csio.cdb_io.cdb_bytes, cmd, cmdlen);
+
+ /* Send the command down via the CAM interface */
+ if (cam_send_ccb(cam_dev, &ccb) < 0) {
+ err(1, "cam_send_ccb");
+ }
+
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
+ return 0;
+
+ /* print error using the uscsi_sense routines? */
+
+ cam_sense = (ccb.ccb_h.status & (CAM_STATUS_MASK | CAM_AUTOSNS_VALID));
+ if (cam_sense != (CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID))
+ return EFAULT;
+
+ /* drive responds with sense information */
+ if (!uscsilib_verbose)
+ return EFAULT;
+
+ /* print sense info */
+ cam_sense_data = &ccb.csio.sense_data;
+ if (uscsi_sense) {
+ uscsi_sense->asc = cam_sense_data->add_sense_code;
+ uscsi_sense->ascq = cam_sense_data->add_sense_code_qual;
+ keypos = cam_sense_data->sense_key_spec;
+ uscsi_sense->skey_valid = keypos[0] & 128;
+ uscsi_sense->sense_key = (keypos[1] << 8) | (keypos[2]);
+ }
+
+ uscsi_print_sense((char *) disc->dev_name,
+ cmd, cmdlen,
+ (uint8_t *) cam_sense_data, 8 + cam_sense_data->extra_len, 1);
+
+ return EFAULT;
+}
+
+
+int
+uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr)
+{
+ struct cam_device *cam_dev;
+
+ /* clean result */
+ bzero(saddr, sizeof(struct uscsi_addr));
+
+ cam_dev = (struct cam_device *) disc->devhandle;
+ if (!cam_dev) return ENODEV;
+
+ /* check if its really SCSI or emulated SCSI (ATAPI f.e.) ? */
+ saddr->type = USCSI_TYPE_SCSI;
+ saddr->addr.scsi.target = cam_dev->target_id;
+ saddr->addr.scsi.lun = cam_dev->target_lun;
+ saddr->addr.scsi.scbus = cam_dev->bus_id;
+
+ return 0;
+}
+
+
+#endif /* USCSI_FREEBSD_CAM */
+
+
+/*
+ * Checks if SCSI is available by issueing the obligatory INQUIRY.
+ */
+
+int
+uscsi_check_for_scsi(struct uscsi_dev *disc)
+{
+ scsicmd cmd;
+ uint8_t buf[256];
+ int error;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[0] = 0x12; /* INQUIRY */
+ cmd[1] = 0; /* basic inquiry */
+ cmd[2] = 0; /* no page or operation code */
+ cmd[3] = 0; /* reserved/MSB result */
+ cmd[4] = 96; /* all but vendor specific */
+ cmd[5] = 0; /* control */
+ error = uscsi_command(SCSI_READCMD, disc, cmd, 6, buf, 96, 30000, NULL);
+
+ return error;
+}
+
+
+/*
+ * Generic SCSI funtions also used by the sense printing functionality.
+ * FreeBSD support has it allready asked for by the CAM.
+ */
+int
+uscsi_mode_sense(struct uscsi_dev *dev,
+ uint8_t pgcode, uint8_t pctl, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(buf, len); /* initialise recieving buffer */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x1a; /* MODE SENSE */
+ cmd[ 1] = 0; /* - */
+ cmd[ 2] = pgcode | pctl; /* page code and control flags */
+ cmd[ 3] = 0; /* - */
+ cmd[ 4] = len; /* length of recieve buffer */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_READCMD, dev, &cmd, 6, buf, len, 10000, NULL);
+}
+
+
+int
+uscsi_mode_select(struct uscsi_dev *dev,
+ uint8_t byte2, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x15; /* MODE SELECT */
+ cmd[ 1] = 0x10 | byte2; /* SCSI-2 page format select */
+ cmd[ 4] = len; /* length of page settings */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
+ 10000, NULL);
+}
+
+
+int
+uscsi_request_sense(struct uscsi_dev *dev, void *buf, size_t len)
+{
+ scsicmd cmd;
+
+ bzero(buf, len); /* initialise recieving buffer */
+
+ bzero(cmd, SCSI_CMD_LEN);
+ cmd[ 0] = 0x03; /* REQUEST SENSE */
+ cmd[ 4] = len; /* length of data to be read */
+ cmd[ 5] = 0; /* control */
+
+ return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len,
+ 10000, NULL);
+}
+
+
+/* end of uscsi_subr.c */
+
diff --git a/uscsilib.h b/uscsilib.h
new file mode 100644
index 0000000..cae3a02
--- /dev/null
+++ b/uscsilib.h
@@ -0,0 +1,116 @@
+/* $NetBSD: extern.h,v 1.4 2002/06/26 16:04:11 mjacob Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Small changes made by Reinoud Zandijk <reinoud@netbsd.org>
+ *
+ */
+
+#ifndef _SCSILIB_H_
+#define _SCSILIB_H_
+
+
+#define SCSI_CMD_LEN 12
+typedef unsigned char scsicmd[SCSI_CMD_LEN];
+
+#include "defs.h"
+#include "uscsilib_machdep.h"
+extern int uscsilib_verbose;
+
+
+/*
+ * Unified structure copied and modified from NetBSD's <sys/scsiio.h> for ease
+ */
+
+struct uscsi_addr {
+ int type; /* bus type */
+#define USCSI_TYPE_SCSI 0
+#define USCSI_TYPE_ATAPI 1
+#define USCSI_TYPE_UNKNOWN 2
+ union {
+ struct {
+ int scbus; /* -1 if wildcard */
+ int target; /* -1 if wildcard */
+ int lun; /* -1 if wildcard */
+ } scsi;
+ struct {
+ int atbus; /* -1 if wildcard */
+ int drive; /* -1 if wildcard */
+ } atapi;
+ } addr;
+};
+
+
+struct uscsi_sense {
+ int asc; /* Additional sense code */
+ int ascq; /* Additional sense code quality */
+ int skey_valid; /* sense key valid */
+ int sense_key; /* sense key; interpret on (asc, ascq) pair */
+};
+
+
+struct uscsi_dev {
+ char *dev_name;
+ int fhandle;
+ void *devhandle; /* for if a fhandle is not enough */
+};
+
+
+/* uscsi_sense.c */
+extern char *uscsi_decode_sense(void *sinfo, int flag);
+extern void uscsi_print_sense(const char *name, u_char *req_cmd,
+ int req_cmdlen, u_char *req_sense, int req_senselen_used,
+ int verbosity);
+
+
+/* scsi_subr.c */
+extern int uscsi_open(struct uscsi_dev *);
+extern int uscsi_close(struct uscsi_dev *);
+extern int uscsi_command(int flags, struct uscsi_dev *disc,
+ void *cmd, size_t cmdlen, void *data, size_t datalen,
+ uint32_t timeout, struct uscsi_sense *uscsi_sense);
+extern int uscsi_check_for_scsi(struct uscsi_dev *);
+extern int uscsi_identify(struct uscsi_dev *, struct uscsi_addr *saddr);
+
+extern int uscsi_mode_sense(struct uscsi_dev *, u_int8_t, u_int8_t,
+ void *, size_t);
+extern int uscsi_mode_select(struct uscsi_dev *, u_int8_t, void *, size_t);
+extern int uscsi_request_sense(struct uscsi_dev *, void *, size_t);
+
+
+#endif /* _SCSILIB_H_ */
+
diff --git a/uscsilib_machdep.h b/uscsilib_machdep.h
new file mode 100644
index 0000000..8d51b1e
--- /dev/null
+++ b/uscsilib_machdep.h
@@ -0,0 +1,91 @@
+/*
+ * File "cd_disect.c" is part of the UDFclient toolkit.
+ * File $Id: uscsilib_machdep.h,v 1.3 2011/02/01 20:43:41 reinoud Exp $ $Name: $
+ *
+ * Copyright (c) 2003, 2004, 2005, 2006, 2011
+ * Reinoud Zandijk <reinoud@netbsd.org>
+ * All rights reserved.
+ *
+ * The UDFclient toolkit is distributed under the Clarified Artistic Licence.
+ * A copy of the licence is included in the distribution as
+ * `LICENCE.clearified.artistic' and a copy of the licence can also be
+ * requested at the GNU foundantion's website.
+ *
+ * Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef _USCSILIB_MACHDEP_H_
+#define _USCSILIB_MACHDEP_H_
+
+#ifndef _DEV_SCSIPI_SCSIPI_ALL_H_
+# define SSD_KEY 0x0F
+# define SSD_ILI 0x20
+# define SSD_EOM 0x40
+# define SSD_FILEMARK 0x80
+# define SKEY_NO_SENSE 0x00
+# define SKEY_RECOVERED_ERROR 0x01
+# define SKEY_NOT_READY 0x02
+# define SKEY_MEDIUM_ERROR 0x03
+# define SKEY_HARDWARE_ERROR 0x04
+# define SKEY_ILLEGAL_REQUEST 0x05
+# define SKEY_UNIT_ATTENTION 0x06
+# define SKEY_WRITE_PROTECT 0x07
+# define SKEY_BLANK_CHECK 0x08
+# define SKEY_VENDOR_UNIQUE 0x09
+# define SKEY_COPY_ABORTED 0x0A
+# define SKEY_ABORTED_COMMAND 0x0B
+# define SKEY_EQUAL 0x0C
+# define SKEY_VOLUME_OVERFLOW 0x0D
+# define SKEY_MISCOMPARE 0x0E
+# define SKEY_RESERVED 0x0F
+#endif
+
+
+#ifndef SCSI
+# define SCSI_READCMD 0
+# define SCSI_WRITECMD 0
+struct scsi_addr;
+int scsilib_verbose;
+#endif
+
+
+#ifdef USCSI_SCSIPI
+# include <sys/scsiio.h>
+# define SCSI_READCMD SCCMD_READ
+# define SCSI_WRITECMD SCCMD_WRITE
+# define SCSI_NODATACMD SCCMD_WRITE
+#endif
+
+
+#ifdef USCSI_LINUX_SCSI
+# include <scsi/sg.h>
+# define SCSI_READCMD SG_DXFER_FROM_DEV
+# define SCSI_WRITECMD SG_DXFER_TO_DEV
+# define SCSI_NODATACMD SC_DXFER_NONE
+#endif
+
+
+#ifdef USCSI_FREEBSD_CAM
+# include <camlib.h>
+# include <cam/scsi/scsi_message.h>
+# define SCSI_READCMD 1
+# define SCSI_WRITECMD 2
+# define SCSI_NODATACMD 0
+#endif
+
+
+#endif /* _USCSILIB_MACHDEP_H_ */
+
diff --git a/vfs_dirhash.c b/vfs_dirhash.c
new file mode 100644
index 0000000..a74922f
--- /dev/null
+++ b/vfs_dirhash.c
@@ -0,0 +1,494 @@
+/* $NetBSD: vfs_dirhash.c,v 1.8 2008/10/31 16:04:59 reinoud Exp $ */
+
+/*
+ * Copyright (c) 2008, 2011 Reinoud Zandijk
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#if 0
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: vfs_dirhash.c,v 1.8 2008/10/31 16:04:59 reinoud Exp $");
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <assert.h>
+#include <dirent.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "uscsilib.h"
+#include "queue.h"
+#include "hash.h"
+#include "dirhash.h"
+
+
+#if 1
+# define DPRINTF(a) ;
+#else
+# define DPRINTF(a) printf(a);
+#endif
+
+/* compat */
+#define KASSERT(a) assert(a)
+#define mutex_enter(mtx) pthread_mutex_lock(mtx)
+#define mutex_exit(mtx) pthread_mutex_unlock(mtx)
+
+
+/*
+ * The locking protocol of the dirhash structures is fairly simple:
+ *
+ * The global dirhash_queue is protected by the dirhashmutex. This lock is
+ * internal only and is FS/mountpoint/vnode independent. On exit of the
+ * exported functions this mutex is not helt.
+ *
+ * The dirhash structure is considered part of the vnode/inode/udf_node
+ * structure and will thus use the lock that protects that vnode/inode.
+ *
+ * The dirhash entries are considered part of the dirhash structure and thus
+ * are on the same lock.
+ */
+
+//static struct sysctllog *sysctl_log;
+//static struct pool dirhash_pool;
+//static struct pool dirhash_entry_pool;
+
+static pthread_mutex_t dirhashmutex;
+static uint32_t maxdirhashsize = DIRHASH_SIZE;
+static uint32_t dirhashsize = 0;
+static TAILQ_HEAD(_dirhash, dirhash) dirhash_queue;
+
+
+void
+dirhash_init(void)
+{
+// const struct sysctlnode *rnode, *cnode;
+// size_t sz;
+// uint32_t max_entries;
+
+ /* initialise dirhash queue */
+ TAILQ_INIT(&dirhash_queue);
+
+ pthread_mutex_init(&dirhashmutex, 0);
+
+#if 0
+ /* init dirhash pools */
+ sz = sizeof(struct dirhash);
+ pool_init(&dirhash_pool, sz, 0, 0, 0,
+ "dirhpl", NULL, IPL_NONE);
+
+ sz = sizeof(struct dirhash_entry);
+ pool_init(&dirhash_entry_pool, sz, 0, 0, 0,
+ "dirhepl", NULL, IPL_NONE);
+
+ mutex_init(&dirhashmutex, MUTEX_DEFAULT, IPL_NONE);
+
+ max_entries = maxdirhashsize / sz;
+ pool_sethiwat(&dirhash_entry_pool, max_entries);
+ dirhashsize = 0;
+
+ /* create sysctl knobs and dials */
+ sysctl_log = NULL;
+ sysctl_createv(&sysctl_log, 0, NULL, &rnode,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_NODE, "dirhash", NULL,
+ NULL, 0, NULL, 0,
+ CTL_VFS, VFS_GENERIC, CTL_CREATE, CTL_EOL);
+ sysctl_createv(&sysctl_log, 0, &rnode, &cnode,
+ CTLFLAG_PERMANENT,
+ CTLTYPE_INT, "memused",
+ SYSCTL_DESCR("current dirhash memory usage"),
+ NULL, 0, &dirhashsize, 0,
+ CTL_CREATE, CTL_EOL);
+ sysctl_createv(&sysctl_log, 0, &rnode, &cnode,
+ CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
+ CTLTYPE_INT, "maxmem",
+ SYSCTL_DESCR("maximum dirhash memory usage"),
+ NULL, 0, &maxdirhashsize, 0,
+ CTL_CREATE, CTL_EOL);
+#endif
+}
+
+
+#if 0
+void
+dirhash_finish(void)
+{
+ pool_destroy(&dirhash_pool);
+ pool_destroy(&dirhash_entry_pool);
+
+ mutex_destroy(&dirhashmutex);
+
+ /* sysctl_teardown(&sysctl_log); */
+}
+#endif
+
+
+/*
+ * generic dirhash implementation
+ */
+
+void
+dirhash_purge_entries(struct dirhash *dirh)
+{
+ struct dirhash_entry *dirh_e;
+ uint32_t hashline;
+
+ if (dirh == NULL)
+ return;
+
+ if (dirh->size == 0)
+ return;
+
+ for (hashline = 0; hashline < DIRHASH_HASHSIZE; hashline++) {
+ dirh_e = LIST_FIRST(&dirh->entries[hashline]);
+ while (dirh_e) {
+ LIST_REMOVE(dirh_e, next);
+ //pool_put(&dirhash_entry_pool, dirh_e);
+ free(dirh_e);
+ dirh_e = LIST_FIRST(&dirh->entries[hashline]);
+ }
+ }
+ dirh_e = LIST_FIRST(&dirh->free_entries);
+
+ while (dirh_e) {
+ LIST_REMOVE(dirh_e, next);
+ //pool_put(&dirhash_entry_pool, dirh_e);
+ free(dirh_e);
+ dirh_e = LIST_FIRST(&dirh->entries[hashline]);
+ }
+
+ dirh->flags &= ~DIRH_COMPLETE;
+ dirh->flags |= DIRH_PURGED;
+
+ dirhashsize -= dirh->size;
+ dirh->size = 0;
+}
+
+
+void
+dirhash_purge(struct dirhash **dirhp)
+{
+ struct dirhash *dirh = *dirhp;
+
+ if (dirh == NULL)
+ return;
+
+ /* purge its entries */
+ dirhash_purge_entries(dirh);
+
+ /* recycle */
+ mutex_enter(&dirhashmutex);
+ TAILQ_REMOVE(&dirhash_queue, dirh, next);
+ mutex_exit(&dirhashmutex);
+
+ //pool_put(&dirhash_pool, dirh);
+ free(dirh);
+ *dirhp = NULL;
+}
+
+
+void
+dirhash_get(struct dirhash **dirhp)
+{
+ struct dirhash *dirh;
+ uint32_t hashline;
+
+ /* if no dirhash was given, allocate one */
+ dirh = *dirhp;
+ if (dirh == NULL) {
+// dirh = pool_get(&dirhash_pool, PR_WAITOK);
+ dirh = malloc(sizeof(struct dirhash));
+ assert(dirh);
+
+ memset(dirh, 0, sizeof(struct dirhash));
+ for (hashline = 0; hashline < DIRHASH_HASHSIZE; hashline++) {
+ LIST_INIT(&dirh->entries[hashline]);
+ }
+ }
+
+ /* implement LRU on the dirhash queue */
+ mutex_enter(&dirhashmutex);
+ if (*dirhp) {
+ /* remove from queue to be requeued */
+ TAILQ_REMOVE(&dirhash_queue, dirh, next);
+ }
+ dirh->refcnt++;
+ TAILQ_INSERT_HEAD(&dirhash_queue, dirh, next);
+ mutex_exit(&dirhashmutex);
+
+ *dirhp = dirh;
+}
+
+
+void
+dirhash_put(struct dirhash *dirh)
+{
+
+ mutex_enter(&dirhashmutex);
+ dirh->refcnt--;
+ mutex_exit(&dirhashmutex);
+}
+
+
+void
+dirhash_enter(struct dirhash *dirh,
+ struct dirent *dirent, uint64_t offset, uint32_t entry_size, int new)
+{
+ struct dirhash *del_dirh, *prev_dirh;
+ struct dirhash_entry *dirh_e;
+ uint32_t hashvalue, hashline;
+ int entrysize;
+
+ /* make sure we have a dirhash to work on */
+ KASSERT(dirh);
+ KASSERT(dirh->refcnt > 0);
+
+ /* are we trying to re-enter an entry? */
+ if (!new && (dirh->flags & DIRH_COMPLETE))
+ return;
+
+ /* calculate our hash */
+ hashvalue = hash32_strn(dirent->d_name, DIRENT_NAMLEN(dirent), HASH32_STR_INIT);
+ hashline = hashvalue & DIRHASH_HASHMASK;
+
+ /* lookup and insert entry if not there yet */
+ LIST_FOREACH(dirh_e, &dirh->entries[hashline], next) {
+ /* check for hash collision */
+ if (dirh_e->hashvalue != hashvalue)
+ continue;
+ if (dirh_e->offset != offset)
+ continue;
+ /* got it already */
+ KASSERT(dirh_e->d_namlen == DIRENT_NAMLEN(dirent));
+ KASSERT(dirh_e->entry_size == entry_size);
+ return;
+ }
+
+ DPRINTF(("dirhash enter %"PRIu64", %d, %d for `%*.*s`\n",
+ offset, entry_size, DIRENT_NAMLEN(dirent),
+ DIRENT_NAMLEN(dirent), DIRENT_NAMLEN(dirent), dirent->d_name));
+
+ /* check if entry is in free space list */
+ LIST_FOREACH(dirh_e, &dirh->free_entries, next) {
+ if (dirh_e->offset == offset) {
+ DPRINTF(("\tremoving free entry\n"));
+ LIST_REMOVE(dirh_e, next);
+ //pool_put(&dirhash_entry_pool, dirh_e);
+ free(dirh_e);
+ break;
+ }
+ }
+
+ /* ensure we are not passing the dirhash limit */
+ entrysize = sizeof(struct dirhash_entry);
+ if (dirhashsize + entrysize > maxdirhashsize) {
+ /* we walk the dirhash_queue, so need to lock it */
+ mutex_enter(&dirhashmutex);
+ del_dirh = TAILQ_LAST(&dirhash_queue, _dirhash);
+ KASSERT(del_dirh);
+ while (dirhashsize + entrysize > maxdirhashsize) {
+ /* no use trying to delete myself */
+ if (del_dirh == dirh)
+ break;
+ prev_dirh = TAILQ_PREV(del_dirh, _dirhash, next);
+ if (del_dirh->refcnt == 0)
+ dirhash_purge_entries(del_dirh);
+ del_dirh = prev_dirh;
+ }
+ mutex_exit(&dirhashmutex);
+ }
+
+ /* add to the hashline */
+ // dirh_e = pool_get(&dirhash_entry_pool, PR_WAITOK);
+ dirh_e = malloc(sizeof(struct dirhash_entry));
+ assert(dirh_e);
+
+ memset(dirh_e, 0, sizeof(struct dirhash_entry));
+
+ dirh_e->hashvalue = hashvalue;
+ dirh_e->offset = offset;
+ dirh_e->d_namlen = DIRENT_NAMLEN(dirent);
+ dirh_e->entry_size = entry_size;
+
+ dirh->size += sizeof(struct dirhash_entry);
+ dirhashsize += sizeof(struct dirhash_entry);
+ LIST_INSERT_HEAD(&dirh->entries[hashline], dirh_e, next);
+}
+
+
+void
+dirhash_enter_freed(struct dirhash *dirh, uint64_t offset,
+ uint32_t entry_size)
+{
+ struct dirhash_entry *dirh_e;
+
+ /* make sure we have a dirhash to work on */
+ KASSERT(dirh);
+ KASSERT(dirh->refcnt > 0);
+
+ /* check for double entry of free space */
+ LIST_FOREACH(dirh_e, &dirh->free_entries, next) {
+ KASSERT(dirh_e->offset != offset);
+ }
+
+ DPRINTF(("dirhash enter FREED %"PRIu64", %d\n",
+ offset, entry_size));
+
+ //dirh_e = pool_get(&dirhash_entry_pool, PR_WAITOK);
+ dirh_e = malloc(sizeof(struct dirhash_entry));
+ assert(dirh_e);
+
+ memset(dirh_e, 0, sizeof(struct dirhash_entry));
+
+ dirh_e->hashvalue = 0; /* not relevant */
+ dirh_e->offset = offset;
+ dirh_e->d_namlen = 0; /* not relevant */
+ dirh_e->entry_size = entry_size;
+
+ /* XXX it might be preferable to append them at the tail */
+ LIST_INSERT_HEAD(&dirh->free_entries, dirh_e, next);
+ dirh->size += sizeof(struct dirhash_entry);
+ dirhashsize += sizeof(struct dirhash_entry);
+}
+
+
+void
+dirhash_mark_freed(struct dirhash *dirh, struct dirhash_entry *dirh_e, struct dirent *dirent)
+{
+ uint64_t offset;
+ uint32_t entry_size;
+
+ /* make sure we have a dirhash to work on */
+ KASSERT(dirh_e);
+ KASSERT(dirh);
+ KASSERT(dirh->refcnt > 0);
+ KASSERT(dirent);
+
+ DPRINTF(("dirhash remove `%*.*s`\n",
+ DIRENT_NAMLEN(dirent), DIRENT_NAMLEN(dirent), dirent->d_name));
+
+ offset = dirh_e->offset;
+ entry_size = dirh_e->entry_size;
+
+ LIST_REMOVE(dirh_e, next);
+ free(dirh_e);
+
+ dirh->size -= sizeof(struct dirhash_entry);
+ dirhashsize -= sizeof(struct dirhash_entry);
+
+ dirhash_enter_freed(dirh, offset, entry_size);
+ return;
+}
+
+
+/*
+ * BUGALERT: don't use result longer than needed, never past the node lock.
+ * Call with NULL *result initially and it will return nonzero if again.
+ */
+int
+dirhash_lookup(struct dirhash *dirh, const char *d_name, int d_namlen,
+ struct dirhash_entry **result)
+{
+ struct dirhash_entry *dirh_e;
+ uint32_t hashvalue, hashline;
+
+ /* make sure we have a dirhash to work on */
+ KASSERT(dirh);
+ KASSERT(dirh->refcnt > 0);
+
+ /* start where we were */
+ if (*result) {
+ dirh_e = *result;
+
+ /* retrieve information to avoid recalculation and advance */
+ hashvalue = dirh_e->hashvalue;
+ dirh_e = LIST_NEXT(*result, next);
+ } else {
+ /* calculate our hash and lookup all entries in hashline */
+ hashvalue = hash32_strn(d_name, d_namlen, HASH32_STR_INIT);
+ hashline = hashvalue & DIRHASH_HASHMASK;
+ dirh_e = LIST_FIRST(&dirh->entries[hashline]);
+ }
+
+ for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) {
+ /* check for hash collision */
+ if (dirh_e->hashvalue != hashvalue)
+ continue;
+ if (dirh_e->d_namlen != (uint32_t) d_namlen)
+ continue;
+ /* might have an entry in the cache */
+ *result = dirh_e;
+ return 1;
+ }
+
+ *result = NULL;
+ return 0;
+}
+
+
+/*
+ * BUGALERT: don't use result longer than needed, never past the node lock.
+ * Call with NULL *result initially and it will return nonzero if again.
+ */
+
+int
+dirhash_lookup_freed(struct dirhash *dirh, uint32_t min_entrysize,
+ struct dirhash_entry **result)
+{
+ struct dirhash_entry *dirh_e;
+
+ /* make sure we have a dirhash to work on */
+ KASSERT(dirh);
+ KASSERT(dirh->refcnt > 0);
+
+ /* start where we were */
+ if (*result) {
+ dirh_e = LIST_NEXT(*result, next);
+ } else {
+ /* lookup all entries that match */
+ dirh_e = LIST_FIRST(&dirh->free_entries);
+ }
+
+ for (; dirh_e; dirh_e = LIST_NEXT(dirh_e, next)) {
+ /* check for minimum size */
+ if (dirh_e->entry_size < min_entrysize)
+ continue;
+ /* might be a candidate */
+ *result = dirh_e;
+ return 1;
+ }
+
+ *result = NULL;
+ return 0;
+}