diff options
author | Andrew Shadura <andrewsh@debian.org> | 2016-03-13 09:42:35 +0100 |
---|---|---|
committer | Andrew Shadura <andrewsh@debian.org> | 2016-03-13 09:42:35 +0100 |
commit | 9178a1bcd66b04269831fac717ae383a22e2dd63 (patch) | |
tree | 018413826c589b9be6d256c40ce10e5e111deab1 |
Imported Upstream version 0.8.1
-rw-r--r-- | LICENCE.clearified.artistic | 136 | ||||
-rw-r--r-- | Makefile.in | 87 | ||||
-rw-r--r-- | Session.vim | 301 | ||||
-rw-r--r-- | cd_disect.c | 2030 | ||||
-rw-r--r-- | cd_sessions.c | 80 | ||||
-rwxr-xr-x | config.guess | 1463 | ||||
-rwxr-xr-x | config.sub | 1555 | ||||
-rwxr-xr-x | configure | 5162 | ||||
-rw-r--r-- | configure.ac | 183 | ||||
-rw-r--r-- | defs.h | 101 | ||||
-rw-r--r-- | dirhash.h | 95 | ||||
-rw-r--r-- | ecma167-udf.h | 844 | ||||
-rw-r--r-- | hash.h | 103 | ||||
-rwxr-xr-x | install-sh | 251 | ||||
-rw-r--r-- | mmc_format.c | 1092 | ||||
-rw-r--r-- | newfs_udf.c | 707 | ||||
-rw-r--r-- | osta.c | 495 | ||||
-rw-r--r-- | osta.h | 45 | ||||
-rw-r--r-- | queue.h | 604 | ||||
-rw-r--r-- | udf.c | 6476 | ||||
-rw-r--r-- | udf.h | 628 | ||||
-rw-r--r-- | udf_allocentries.c | 347 | ||||
-rw-r--r-- | udf_bmap.c | 409 | ||||
-rw-r--r-- | udf_bswap.h | 161 | ||||
-rw-r--r-- | udf_discop.c | 1422 | ||||
-rw-r--r-- | udf_discop.h | 148 | ||||
-rw-r--r-- | udf_readwrite.c | 842 | ||||
-rw-r--r-- | udf_unix.c | 636 | ||||
-rw-r--r-- | udf_unix.h | 165 | ||||
-rw-r--r-- | udf_verbose.c | 1513 | ||||
-rw-r--r-- | udfclient.c | 1781 | ||||
-rw-r--r-- | udfdump.c | 265 | ||||
-rw-r--r-- | uio.c | 87 | ||||
-rw-r--r-- | uio.h | 72 | ||||
-rw-r--r-- | uscsi_sense.c | 835 | ||||
-rw-r--r-- | uscsi_subr.c | 594 | ||||
-rw-r--r-- | uscsi_subr.c.async | 608 | ||||
-rw-r--r-- | uscsilib.h | 116 | ||||
-rw-r--r-- | uscsilib_machdep.h | 91 | ||||
-rw-r--r-- | vfs_dirhash.c | 494 |
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" + @@ -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_ */ + @@ -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; +} + @@ -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 @@ -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_ */ + @@ -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_ */ @@ -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(×pec->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 */ + @@ -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(×[0], &stat.st_atimespec); /* access time */ + TIMESPEC_TO_TIMEVAL(×[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(×[0], &stat.st_atimespec); /* access time */ + TIMESPEC_TO_TIMEVAL(×[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; +} + @@ -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 */ + @@ -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; +} |